pealdevDOCS
Back to peal.dev

AUTH STARTER · SOCIAL PROVIDERS (GOOGLE, GITHUB)

Social providers (Google, GitHub)

OAuth done right. Two providers wired, more in 4 lines each. Callback URLs are the part that trips everyone up.

Google and GitHub are pre-wired. To turn them on, set the env vars and flip the toggle in authConfig. The toggle controls whether the button renders on the sign-in card.

Turn one on

Terminal
# .env.local
GOOGLE_CLIENT_ID="..."
GOOGLE_CLIENT_SECRET="..."
TypeScript
// src/auth.config.ts
socialProviders: {
  google: {
    enabled: true,  // ← flip this
  },
  github: {
    enabled: true,
  },
},

Why enabled is a manual boolean, not derived from env

authConfig is shared between client and server. Server-only env vars (GOOGLE_CLIENT_ID) aren't available on the client, so deriving enabled from process.env on the client returns false → hydration mismatch on render. Manual boolean is intentional, not lazy.

Callback URLs (the gotcha)

When you create the OAuth app on Google or GitHub, you specify a redirect URI. It must match EXACTLY what Better Auth uses:

Terminal
# Dev
http://localhost:3000/api/auth/callback/google
http://localhost:3000/api/auth/callback/github

# Prod
https://yourdomain.com/api/auth/callback/google
https://yourdomain.com/api/auth/callback/github

Trailing slashes break OAuth

A trailing slash in the callback URL on Google's side that isn't in the URL Better Auth sends → redirect_uri_mismatch. We've debugged this twice. No trailing slash.

Adding a new provider (Discord example)

Better Auth supports Discord, Apple, Twitter/X, Facebook, Microsoft, and more. The pattern is identical:

TypeScript
// 1. src/lib/env.ts — declare
DISCORD_CLIENT_ID: z.string().optional(),
DISCORD_CLIENT_SECRET: z.string().optional(),

// 2. src/auth.config.server.ts — wire creds
discord: {
  enabled: Boolean(env.DISCORD_CLIENT_ID && env.DISCORD_CLIENT_SECRET),
  clientId: env.DISCORD_CLIENT_ID ?? "",
  clientSecret: env.DISCORD_CLIENT_SECRET ?? "",
  scopes: ["identify", "email"],
},

// 3. src/auth.config.ts — UI toggle
socialProviders: {
  discord: { enabled: true },
  // ...
},

// 4. src/lib/auth.ts — add to socialProviders map (mirror the google/github blocks)

Scopes matter

Different providers need different scopes. Google needs email + profile. GitHub needs read:user + user:email (otherwise email comes back null for users with private emails). Always test sign-up with a fresh account before launch.