Google OAuth — Setup Guide
"Continue with Google" buttons live on /auth/signin and /auth/signup, and a "Link Google" affordance on /dashboard/settings. The flow uses Supabase's PKCE OAuth handshake — no env vars on the application side, all credentials live in the Supabase dashboard. This guide walks the one-time setup.
For the architectural detail (PKCE code-verifier cookie handling, server route handler, collision recovery, brand-aware redirects), see the Authentication section of ../ai/INDEX.md.
1. Create a Google OAuth client
You need a single OAuth 2.0 Client in Google Cloud Console that Supabase will use to authenticate users.
- Go to https://console.cloud.google.com/apis/credentials
- Create or select a project (a single project for HiveJournal is fine — the OAuth client is the unit of identity, not the project).
- Click + CREATE CREDENTIALS → OAuth client ID.
- If prompted, configure the OAuth consent screen first:
- User type: External
- App name: HiveJournal
- User support email + Developer contact: your email
- Authorized domains:
hivejournal.com,graphene.fm,write.cafe,lovio.io,dreampro.io - Scopes: leave default (email + profile + openid). No sensitive scopes needed.
- Back on Create OAuth client ID:
- Application type: Web application
- Name:
HiveJournal Supabase Auth(or similar) - Authorized redirect URIs: add exactly one — Supabase's callback URL:
You'll find this string in the Supabase dashboard under Authentication → Providers → Google (the "Callback URL (for OAuth)" field). Copy it verbatim.https://<your-supabase-project-ref>.supabase.co/auth/v1/callback - Click CREATE.
- Copy the Client ID and Client secret from the modal that appears.
Common mistake: adding the brand-domain
/auth/callbackpaths to the Google redirect URI list. Those go in Supabase, not Google. Google only ever talks to Supabase; Supabase then redirects to the brand domain.
2. Wire the provider in Supabase
- Open the Supabase dashboard → your project → Authentication → Providers → Google.
- Toggle Enable Sign in with Google ON.
- Paste the Google Client ID into the Client IDs field.
- Paste the Client secret into Client Secret (for OAuth).
- Leave Skip nonce checks and Allow users without an email OFF — the defaults are correct.
- Click Save at the bottom of the panel.
3. Allow the brand-domain callback URLs
Our app passes redirectTo: https://<brand-domain>/auth/callback to signInWithOAuth. Supabase rejects any redirectTo value that isn't on its Redirect URLs allowlist (security floor — prevents OAuth-based open-redirect attacks). Without this step, the user clicks Google on write.cafe and lands silently on hivejournal.com (Supabase's Site URL fallback).
-
Open Authentication → URL Configuration.
-
Under Redirect URLs, add a wildcard per host (one per line):
https://www.hivejournal.com/** https://hivejournal.com/** https://www.graphene.fm/** https://graphene.fm/** https://www.write.cafe/** https://write.cafe/** https://www.lovio.io/** https://lovio.io/** https://www.dreampro.io/** https://dreampro.io/** http://localhost:3000/**The
wwwand bare-host entries are separate origins for cookie purposes, so both are needed unless you redirect one to the other at the DNS/Vercel layer. The localhost entry is for dev.Use
/**, not just/auth/callback. OAuth only needs/auth/callback, but the same allowlist governs email confirmation (redirects to/dashboard,/lovio, …) and password reset (/auth/reset-password). Enumerating only/auth/callbackworks for Google but silently breaks the email-confirmation flow once it's enabled — the confirm link's/dashboardtarget isn't matched, Supabase falls back to the Site URL, and the user lands on the wrong host. The wildcard covers every flow. (See the email-link note in RESEND_SMTP_SUPABASE_SETUP.md.)Every branded rewrite domain needs its entries here. A host can be wired into the frontend (
middleware.tsrewrite) and backend (PLATFORM_ORIGINSCORS) yet still be broken for auth if it's missing from this allowlist — Supabase falls back to the hivejournal.com Site URL, the session never lands on the originating host, and the client-side/dashboardguard bounces every signup to/auth/signin. This is exactly how lovio.io shipped: 100% of its signups bounced and 0 activated until it was added here. When you add a domain toMULTI_BRAND_DOMAINS.md, add it to this list and to Authorized domains above in the same change. -
Click Save.
Common mistake: only adding
https://www.<brand>.com/auth/callback. A user who visits the bare host gets a Site URL fallback and the session lands on the wrong domain. The PKCE code-verifier cookie was set on the originating host, so the wrong-domain callback can't decode the code → "invalid request: both auth code and code verifier should be non-empty."
4. (Optional) Enable manual identity linking
If you want the "Link Google" button on /dashboard/settings to actually attach a Google identity to an existing email-password user (instead of erroring), enable manual linking:
- Authentication → Settings.
- Find Manual linking enabled and toggle it ON.
- Save.
Without this, supabase.auth.linkIdentity() returns an actionable in-card error explaining the missing config. The OAuth signin / signup paths still work fine without it — only the link-from-settings affordance requires it.
5. Smoke test
- Visit
https://www.hivejournal.com/auth/signin(orhttps://www.write.cafe/auth/signin, etc.). - Click Continue with Google.
- Pick a Google account. You should be redirected back to the brand domain's
/auth/callback, then to/dashboard(or/graphene//write-cafe), signed in. - From
/dashboard/settings, the Sign-in methods card should show both Email + password ✓ Connected and Google ✓ Connected.
If something goes wrong, the symptoms map to causes:
| Symptom | Likely cause |
|---|---|
| Lands on hivejournal.com instead of the brand domain you started on | Brand-domain /auth/callback URL missing from Supabase Redirect URLs allowlist |
| "Sign-in failed: invalid request: both auth code and code verifier should be non-empty" | Same as above — Supabase fell back to Site URL and the PKCE cookie was set on the originating host |
| "Account linking is not enabled on this project" | Manual linking not turned on in Supabase Auth → Settings |
| "An account with this email already exists" notice on /auth/signin | Working as intended — the existing email-password user is being routed to sign in with their password, then link Google from settings (security floor — prevents OAuth-based takeover of unverified accounts) |
| Google sign-in works on hivejournal.com but not write.cafe | Same allowlist issue scoped to a specific brand |