Google sign-in/up integration using Google Identity Services (accounts.google.com/gsi/client). The flow short-circuits email verification because Google accounts are pre-verified.
Actors
User with a Google account.
Google Identity Services (GSI) — JS SDK loaded on demand by the frontend service.
Frontend — frontend/src/auth/services/google-oauth.ts, consumed by frontend/src/auth/view/jwt/jwt-sign-in-view.tsx and jwt-sign-up-view.tsx.
Backend — AuthController.googleSignUp and googleSignIn in backend/src/services/auth/authController.ts, backed by googleOAuthService.verifyGoogleToken() in backend/src/services/auth/googleOAuthService.ts.
MongoDB — User collection (account linking by email).
Preconditions
NEXT_PUBLIC_GOOGLE_CLIENT_ID is set on the frontend (frontend/src/auth/services/google-oauth.ts line 2 — there is a hard-coded fallback for the dev project ID).
Same client ID is whitelisted on the backend googleOAuthService.
The current origin is registered under "Authorized JavaScript origins" in Google Cloud Console (see frontend/GOOGLE_OAUTH_SETUP.md).
Step-by-step narrative
Sign-up
User clicks the Google icon on /auth/jwt/sign-up. The form is configured with the chosen role (buyer/seller) and an optional referral code.
The frontend lazy-loads https://accounts.google.com/gsi/client if it is not yet on window.google.
google.accounts.oauth2.initTokenClient({ client_id, scope: 'openid email profile' }) is initialised, and .requestAccessToken() opens the Google popup.
On success the popup returns an ID token (a Google-signed JWT containing email, email_verified, name, given_name, family_name, picture, sub).
Frontend calls signUpWithGoogle({ googleToken, role, referralCode }) (frontend/src/auth/context/jwt/action.ts:281-304), which POSTs POST /api/auth/google/signup.
Backend authController.googleSignUp (:781-872) calls googleOAuthService.verifyGoogleToken(googleToken). The verifier uses google-auth-library to validate the JWT signature, expiry, audience (client_id), and issuer.
Duplicate check: User.findOne({ email: googleUser.email }) — if found returns 409 USER_EXISTS so the user can use sign-in instead.
New user creation with password omitted, isEmailVerified: true, status: "active", profile.avatar = googleUser.picture, role from the request.
Referral attribution (authController.ts:817-838): same logic as the email path — increment referrer.referralStats.totalReferrals, emit referral-signup on user-${referrer._id}.
Generate access + refresh tokens, push refresh into user.refreshTokens[], respond with { user, tokens }.
Frontend stores tokens in localStorage and redirects to the dashboard.
Sign-in
User clicks the Google icon on /auth/jwt/sign-in.
Same GSI flow as sign-up — Google returns an ID token.
Frontend calls signInWithGoogle(googleToken) → POST /api/auth/google/signin.
Backend verifies the token, looks up User.findOne({ email: googleUser.email }). If no user, returns 404 USER_NOT_FOUND ("please sign up first"). The frontend surfaces a localized prompt.
On hit: existingUser.lastLoginAt = now; if profile.avatar is empty and Google has a picture, it is back-filled (authController.ts:905-907).
Tokens issued and returned identically to email login.
[!tip] Account linking is implicit by email
A user who originally signed up via email + password can sign in with Google as long as the email matches — no extra "link account" step. The backend simply reuses the existing user document. There is no separate googleId field stored today, so this is a one-way trust on googleUser.email.
Sequence diagram
sequenceDiagram
autonumber
actor U as User
participant FE as Frontend
participant G as Google GSI
participant BE as Backend
participant GA as google-auth-library
participant DB as MongoDB
U->>FE: Click Google icon
FE->>G: load gsi/client, initTokenClient(client_id)
FE->>G: requestAccessToken()
G-->>U: Popup → consent
U-->>G: Approve
G-->>FE: ID token (signed JWT)
alt Sign-up
FE->>BE: POST /api/auth/google/signup { googleToken, role, referralCode }
else Sign-in
FE->>BE: POST /api/auth/google/signin { googleToken }
end
BE->>GA: verifyGoogleToken(googleToken)
GA-->>BE: { email, name, picture, ... } or null
BE->>DB: User.findOne({ email })
alt Sign-up: user exists
BE-->>FE: 409 USER_EXISTS
else Sign-up: new
BE->>DB: User.create({ email, role, isEmailVerified:true, profile.avatar })
opt referral
BE->>DB: increment referrer.referralStats
end
else Sign-in: user missing
BE-->>FE: 404 USER_NOT_FOUND
else Sign-in: ok
BE->>DB: user.lastLoginAt = now; back-fill avatar if blank
end
BE->>BE: generate access + refresh; push refresh
BE-->>FE: 200 { user, tokens }
FE->>FE: localStorage.setItem(accessToken/refreshToken)
FE-->>U: Redirect /dashboard/{role}
API calls
Method
Endpoint
Source
POST
/api/auth/google/signup
authRoutes.ts:30 → authController.googleSignUp
POST
/api/auth/google/signin
authRoutes.ts:31 → authController.googleSignIn
Database writes
users collection: on sign-up, full insert (no password). On sign-in, only lastLoginAt, possibly profile.avatar, and a new refresh token appended.
Socket events emitted
referral-signup → user-${referrerId} when sign-up includes a valid referralCode.
Side effects
No email is sent (Google handles trust). No TempVerification is created.
The avatar URL is stored from Google's CDN; consider proxying or rehosting if Google's privacy rules change for googleusercontent.com.
User already exists during sign-up → 409; frontend prompts to use sign-in instead.
User missing during sign-in → 404; frontend redirects to sign-up.
Popup blocker → GSI throws a client-side error caught in the view and surfaced as a toast.
Network failure to accounts.google.com → GSI rejects; frontend retries on next click.
email_verified === false on the Google token → currently not enforced; the backend trusts any successful Google response. For an extra-strict mode, gate on googleUser.email_verified === true in googleOAuthService.
[!warning] Single backup file
frontend/src/auth/services/google-oauth.ts.backup is checked in. Delete or convert to a documentation note — it leaks a hard-coded client ID that should only live in .env.*.
Linked flows
Authentication Flow — token issuance and storage are identical from step 9 onward.
Registration Flow — alternative path that requires email verification.
Referral Flow — works identically for Google-signup referrals.