OAuth & Social Login
Sign in or register using social providers — currently Google and Facebook in the UI; Apple and GitHub supported by the backend
OAuth & Social Login
EduShade lets users sign in or register using their existing social accounts — eliminating another password to remember and speeding up onboarding.
Supported Providers
There are two layers to be aware of:
| Provider | Backend (oauth_service.go) | Frontend (UI button on login/register) |
|---|---|---|
| ✅ Implemented | ✅ Visible | |
| ✅ Implemented | ✅ Visible | |
| GitHub | ✅ Implemented | ❌ No button rendered |
| Apple | ✅ Implemented | ❌ Button code exists but is commented out |
What end users actually see today: only Google and Facebook buttons on
/auth/loginand/auth/register. Apple and GitHub are wired in the backend OAuth handler and can be enabled by uncommenting the Apple button or adding a GitHub button insrc/components/auth/oauth-buttons.tsx, plus enabling the provider inconfig.yaml.
Scopes & Data Retrieved (when enabled)
| Provider | Typical Scopes | Data Retrieved |
|---|---|---|
openid, email, profile | Name, Email, Profile Picture, Locale | |
email, public_profile | Name, Email, Profile Picture | |
| GitHub | user:email | Username, Email, Name, Avatar |
| Apple | name, email | Name, Email |
Provider availability is also gated by tenant/deployment config — if a provider's client_id and client_secret aren't configured, its endpoint will reject the request even if a button is shown.
How Social Login Works
Step-by-Step Flow
- Click the provider button on the login or register page.
- Frontend calls
GET /v1/oauth/{provider}and receives aredirect_url. - Browser navigates to the provider's authorization page.
- User grants EduShade permission to access their basic profile.
- Provider redirects back to
/auth/oauth/{provider}/callback?code=...&state=.... - Frontend posts the
codeandstatetoPOST /v1/oauth/{provider}/callback. - Backend resolves the account:
- Provider ID already linked → log in
- Email matches an existing account → link provider, then log in
- Neither → create new account, then log in
- Tokens are returned and stored; the user is redirected to their dashboard (or the
intendedURL captured before the OAuth flow).
What Gets Auto-Populated on First Sign-Up
| Field | Source |
|---|---|
| Name | Social profile |
| Social profile (auto-verified if the provider confirms it) | |
| Avatar | Profile picture URL |
| Provider ID | Stored as google_id, facebook_id, github_id, or apple_id for future logins |
Security
State Parameter Protection
The OAuth flow uses an encrypted state parameter to prevent CSRF and replay attacks. The state encodes: tenant ID, provider, a cryptographic token, and an expiry timestamp. On callback the state is validated for:
- Correct tenant (prevents cross-tenant attacks)
- Untampered token
- Not expired (prevents replay)
Email Auto-Verification
If the provider confirms the email is verified (e.g. Google), EduShade marks the user's email as verified automatically — the user skips the email verification step.
Intent Preservation
Before redirecting to the provider, the frontend stores any intended query param in sessionStorage under es_auth_oauth_intended so the user lands on the page they originally tried to reach after signing in.
Linking Social Accounts
You can link multiple social accounts to one EduShade account.
How to Link
- Log in to EduShade.
- Go to Account Settings → connected accounts section.
- Click Connect next to a provider.
- Complete the provider's authorization screen.
Backend endpoint: POST /v1/oauth/link/{provider} (authenticated).
How to Unlink
- Go to Account Settings → connected accounts.
- Click Disconnect next to the provider.
Backend endpoint: DELETE /v1/oauth/unlink/{provider} (authenticated).
You must always have at least one login method available (a password or another linked provider) before unlinking — otherwise you would lock yourself out.
Admin-Initiated Linking
The auth-service exposes admin link/unlink endpoints (POST /v1/users/:user_id/oauth/link and POST /v1/users/:user_id/oauth/unlink, both requiring user.update). These accept a provider_user_id and are designed primarily for service-to-service or scripted account migration — admins typically don't have a user's raw provider ID at hand, so there is no general-purpose UI for arbitrary linking.
Troubleshooting
| Issue | Solution |
|---|---|
| "Authorization failed" | The provider denied access. Try again and make sure to click "Allow" |
| "Invalid state" | The OAuth session expired or the state was tampered. Restart the login flow |
| "Email already registered" | An account with this email exists but isn't linked to this provider. Log in with your password (or another linked provider) first, then link this provider from settings |
| Provider button not showing | Only Google and Facebook are visible by default. Apple/GitHub require enabling in the frontend buttons component plus provider config |
| Profile picture not updating | Avatars are pulled at first sign-up. Update your avatar manually in account settings |

