/login and /register light up automatically
once GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are set. There is no provider
code to write — Better Auth handles the OAuth dance, and the auto-link logic in
src/lib/auth.ts merges Google sign-ins with existing email accounts.
Prerequisites
- A Google account.
- Your production domain decided (you can develop locally first).
- 10 minutes — most of it is filling in the OAuth consent screen.
Step-by-step setup
- Open Google Cloud Console at console.cloud.google.com and create (or pick) a project. Each product gets its own project.
-
Configure the OAuth consent screen. Under APIs & Services → OAuth consent
screen, pick “External” user type. Fill in app name, support email, and a logo.
The scopes you need are the defaults:
userinfo.email,userinfo.profile,openid. Add your production domain as an authorized domain. - Create OAuth 2.0 credentials. APIs & Services → Credentials → Create Credentials → OAuth client ID. Application type: Web application.
-
Set authorized redirect URIs. Better Auth’s callback path is
/api/auth/callback/google. Add both your local and production URIs:If you preview on Vercel, add the preview URL too. -
Copy the client ID + secret into
.env.local: -
Enable the button. In
src/config/site.ts: -
Restart dev server. Env vars are read at startup —
pnpm devafter each.env.localedit.
What happens behind the scenes
When the user clicks “Continue with Google”,authClient.signIn.social({ provider: 'google', callbackURL: '/dashboard' }) runs. Better Auth redirects to Google,
Google bounces back to /api/auth/callback/google, and the callback handler:
- Exchanges the auth code for an access + ID token.
- Looks up the email in
account(providerId = 'google'). If found, sign in. - If not found but the email matches an existing
user, link a newaccountrow (account-linking is on for trusted providers — seesrc/lib/auth.ts). - If no match, create a new
user+accountand fire the post-create hook (which grants register-gift credits and promotes to admin if applicable).
[better-auth] prefix.
Verify it works
- Visit
/login— the “Continue with Google” button should be visible. - Click it. You bounce to
accounts.google.com, pick an account, and land back on/dashboard. - Check the
accounttable — there should be a row withproviderId = 'google'linked to youruserrow. - Sign out and sign back in with Google. Same
userid, no duplicate row.
Common pitfalls
redirect_uri_mismatcherror. You typed the redirect URI wrong in the Google console. Trailing slashes matter;httpvshttpsmatters; localhost port matters. Copy-paste from the list above.- Localhost works, production doesn’t. You only added the localhost URI. Add the production URI to the same OAuth client (you can have many redirect URIs per client).
- OAuth consent screen stuck “Testing”. While in Testing, only the test users you list can sign in. Click Publish App for general availability. Production apps usually do not need verification unless you ask for sensitive scopes.
NEXT_PUBLIC_GOOGLE_CLIENT_IDmissing. Causes One-Tap to silently no-op. See One-Tap docs.- Different domains in dev vs preview vs prod. Each gets its own redirect URI entry. There’s no wildcard support.
- Refresh tokens not requested. Better Auth uses the standard
onlineaccess type — fine for sign-in. If you later need to call Google APIs on the user’s behalf, you’ll need offline access and a refresh token; passaccessType: 'offline'in your provider config.
Official docs
- Using OAuth 2.0 for Web Server Apps — developers.google.com/identity/protocols/oauth2/web-server
- OAuth consent screen setup — support.google.com/cloud/answer/10311615
- Better Auth Google provider — better-auth.com/docs/authentication/google