Acheteur Google OAuth Sign-in
Summary
An acheteur can sign in (or register) using their Google account. The client obtains a Google ID token via Google Identity Services and POSTs it to the API. The system verifies the token, upserts the acheteur row (creating it if new, or linking the Google ID to an existing password-based account), issues session tokens, and returns immediately — no email verification step is required because Google already guarantees the email is valid. If an existing verified account is linked to Google for the first time, a notification email is sent.
State Diagram
stateDiagram-v2 state "Token Google reçu" as TokenReceived state "Token invalide" as TokenInvalid state "Email manquant" as EmailMissing state "Nouveau compte" as NewAccount state "Compte existant non lié" as ExistingUnlinked state "Compte existant lié" as ExistingLinked state "Session créée" as SessionIssued [*] --> TokenReceived: acheteur submits Google credential TokenReceived --> TokenInvalid: Google verification fails TokenInvalid --> [*]: 401 INVALID_CREDENTIAL TokenReceived --> EmailMissing: payload has no email EmailMissing --> [*]: 400 EMAIL_REQUIRED TokenReceived --> NewAccount: no existing acheteur for email TokenReceived --> ExistingUnlinked: account exists, no googleId yet TokenReceived --> ExistingLinked: account exists with googleId NewAccount --> SessionIssued: upsert creates row, emailVerified=true ExistingUnlinked --> SessionIssued: googleId set, notification email sent ExistingLinked --> SessionIssued: googleId refreshed SessionIssued --> [*]: session issued, 200 returned
Steps
1. Submit Google Credential (Actor: acheteur)
Client POSTs { credential: "<Google ID token>" } to POST /acheteur/auth/google.
Rate limit: 10 requests per minute per IP.
Outcome: Request reaches the handler.
2. Verify Google ID Token (Actor: system)
The singleton OAuth2Client (preserves Google’s cert cache across requests) calls verifyIdToken() against GOOGLE_CLIENT_ID. On failure, returns 401 INVALID_CREDENTIAL. If the verified payload contains no email, returns 400 EMAIL_REQUIRED.
Outcome: Validated payload with { email, given_name, family_name, sub }.
3. Upsert Acheteur (Actor: system)
Existing account lookup is performed before the upsert to detect the “first-time Google link” case.
The upsert:
- Create path — sets
emailVerified: true,passwordHash: null,googleIdfrom the tokensub. - Update path — sets
googleId, clearsemailVerifyDeadlineandverifyReminderSentAt, marksemailVerified: true.
If an existing verified account had no googleId before this request, a acheteur-google-linked notification email is fired asynchronously (fire-and-forget; Slack alert on failure).
Outcome: Acheteur row exists and is fully verified.
4. Prune Sessions (Actor: system)
Oldest refresh tokens beyond the 4-session cap are deleted (pruneAcheteurSessions).
Outcome: Session count capped at 4.
5. Issue Tokens (Actor: system)
A 7-day opaque refresh token is generated, hashed, stored in acheteur_refresh_tokens, and set as an httpOnly cookie (acheteurRefreshToken, maxAge 7 days). A 15-minute access token is signed with ACHETEUR_JWT_SECRET.
Outcome: 200 response with { accessToken, acheteur }, refresh token cookie set.
Error States
- Invalid or expired Google credential → 401
INVALID_CREDENTIAL - Google payload missing email → 400
EMAIL_REQUIRED - Notification email send failure → logged + Slack sentry-notif (non-blocking, session still issued)
Related Processes
- registration-email-verification — alternative registration path using password
- profile-management — Google-only accounts have no
passwordHash; email change and password change are blocked for them