My site had exactly one way in: GitHub. Perfect for developers and their agents — but I started onboarding people who aren't developers, and asking a baker to create a GitHub account just to save a favorite is absurd. They all have a Gmail. So I added "Continue with Google."
The web plumbing was the easy half. Two things actually cost time.
Identity. My database keyed every user by their GitHub username. Google users don't have one, and I didn't want ugly URLs like /a/10982374. So on first Google sign-in the person picks a worklore handle, and I map their Google account id to it permanently. The new record types had to be filtered out of every listing so they never show as broken cards — and one of them, a cross-account favorites merge in localStorage, bit me in testing: a new user inherited the previous user's saved list.
The Google Console. The docs make it sound like "create an OAuth client." In practice you also fill a Branding screen (logo, home page, and LIVE privacy + terms URLs), add your authorized domain, and — the step nobody mentions — flip the consent screen from Testing to Production, past a "send for verification" button you can ignore as long as you only request the basic openid/email/profile scopes. Miss any of these and real users get "access blocked."
An hour of work, and now anyone with a Gmail can sign in.
Reproduce this
Prerequisites: an app with (or without) one OAuth provider, a backend that can make server-side HTTPS calls, and LIVE privacy + terms pages on your domain. Your agent will need from you: which provider, your domain, and how a user is keyed today. Steps:
- Register the OAuth app — Google Cloud Console, in full:
- Create a project: https://console.cloud.google.com/projectcreate
- Branding / OAuth consent screen: app name, a square logo, Application home page, a LIVE privacy-policy URL and Terms URL, and your Authorized domain.
- Scopes: request only openid, email, profile (non-sensitive → no verification review, no user cap, no "unverified app" screen).
- Credentials → OAuth client ID → Web application: Authorized JS origin https://example.com and redirect URI https://example.com/auth/google/callback.
- Publish: switch Testing → Production ("Publish app"). Do NOT "send for verification" for basic scopes — the button is there but not required.
- Store the client secret in a secrets manager (e.g. AWS SSM SecureString), never in code; the client ID is public and can ship in the frontend.
- Backend, server-side: POST code + client_id + client_secret + redirect_uri + grant_type=authorization_code to https://oauth2.googleapis.com/token, then GET https://www.googleapis.com/oauth2/v3/userinfo with the access_token to read sub, email, name, picture. No JWT library needed.
- Identity: key the account by the provider's stable
sub, not email. If your users have no natural handle, have them choose one on first sign-in and map provider-sub → handle. Exclude any new record types from every listing. - Frontend: one sign-in page with a button per provider + Terms/Privacy links; the button redirects with response_type=code, scope, and an anti-forgery state; the callback posts the code to your backend and stores the session.
Verify: sign in with a brand-new provider account in a private window — a new user record is created and the session persists on reload. Then sign out and sign in as a DIFFERENT account and confirm no data (favorites, drafts) carries across. Open the consent screen as a non-test user to confirm it's really published (no "access blocked").