Handle OAuth 2.0 authorization callbacks, token exchange, and user profile retrieval for all providers
Callback endpoints complete the OAuth 2.0 flow by receiving the authorization code from providers, exchanging it for access tokens, and creating or updating user identities.
Source:src/app/api/auth/callback/route.ts:22-126Token Exchange:exchangeCodeForTokens() in src/lib/secondme.ts:30-68Profile Fetch:getUserProfile() in src/lib/secondme.ts:70-95Token Endpoint:https://app.mindos.com/gate/lab/api/oauth/token/codeUser Info Endpoint:https://app.mindos.com/gate/lab/api/secondme/user/info
POST https://app.mindos.com/gate/lab/api/oauth/token/codeContent-Type: application/x-www-form-urlencodedgrant_type=authorization_code&code={auth_code}&redirect_uri={redirect_uri}&client_id={client_id}&client_secret={client_secret}
Source:src/app/api/auth/callback/github/route.ts:22-123Token Exchange:exchangeGitHubCode() in src/lib/oauth.ts:33-61Profile Fetch:getGitHubProfile() in src/lib/oauth.ts:63-98Token Endpoint:https://github.com/login/oauth/access_tokenUser Info Endpoint:https://api.github.com/userEmails Endpoint:https://api.github.com/user/emails
Source:src/app/api/auth/callback/google/route.ts:22-124Token Exchange:exchangeGoogleCode() in src/lib/oauth.ts:114-157Profile Fetch:getGoogleProfile() in src/lib/oauth.ts:159-183Token Endpoint:https://oauth2.googleapis.com/tokenUser Info Endpoint:https://www.googleapis.com/oauth2/v3/userinfo
POST https://oauth2.googleapis.com/tokenContent-Type: application/x-www-form-urlencodedcode={auth_code}&client_id={client_id}&client_secret={client_secret}&redirect_uri={redirect_uri}&grant_type=authorization_code
State Validation is CriticalAll callbacks MUST validate the state parameter matches the stored cookie. Missing or mismatched state indicates a CSRF attack.
if (!state || !storedState || state !== storedState) { return NextResponse.redirect('/?error=invalid_state');}
Identity Conflict PreventionDuring bind flow, the system prevents binding a provider account that’s already linked to a different user:
if (existing?.canonicalUserId && existing.canonicalUserId !== bindTargetUserId) { return NextResponse.redirect('/profile?bind=failed&reason=conflict');}
Cookie CleanupAll callback handlers delete OAuth cookies after processing:
{provider}_oauth_state
{provider}_oauth_origin
{provider}_oauth_redirect_uri
oauth_login_flow
oauth_bind_target_user_id (if applicable)
Token StorageAccess tokens are:
Never exposed to client-side JavaScript
Stored in MongoDB with appropriate encryption at rest
Associated with canonical user ID for multi-provider support
Refreshed as needed (Google and SecondMe support refresh tokens)