Authentication

Google OAuth-first auth (Better Auth) + optional magic links via Resend.

Overview

This starter is set up for Google OAuth as the primary login, powered by Better Auth.

Optional: enable magic-link sign-in and email verification via Resend.

Key files:

  • Better Auth server config: tanstarter/src/auth.ts
  • Better Auth route handler: tanstarter/src/routes/api/auth/$.ts
  • Client SDK: tanstarter/src/lib/auth-client.ts
  • UI: tanstarter/src/routes/login.tsx

Required env (minimum)

APP_URL=http://localhost:3000
VITE_APP_URL=http://localhost:3000
BETTER_AUTH_URL=http://localhost:3000

DATABASE_URL=postgresql://username:password@localhost:5432/database_name
BETTER_AUTH_SECRET=your-32-character-secret-here

Google OAuth (primary setup)

1) Create a Google Cloud project

  1. Open: Google Cloud Console
  2. Create a new project (or select an existing one).

2) Configure OAuth consent screen

  1. Go to APIs & Services → OAuth consent screen
  2. Choose External (most cases) or Internal (Google Workspace only)
  3. Fill out required fields (app name, support email, etc.)
  4. Add scopes (typical defaults are fine to start):
    • openid
    • email
    • profile

Google docs:

3) Create OAuth client credentials

  1. Go to APIs & Services → Credentials
  2. Click Create credentials → OAuth client ID
  3. Choose Web application
  4. Add the URLs below

Google docs:

4) Add authorized URLs (important)

In the OAuth client settings:

Authorized JavaScript origins

  • http://localhost:3000
  • https://yourdomain.com

Authorized redirect URIs

  • http://localhost:3000/api/auth/callback/google
  • https://yourdomain.com/api/auth/callback/google

Why this path?

  • Better Auth defaults to the /api/auth base path on the server.
  • This repo wires the handler at tanstarter/src/routes/api/auth/$.ts.
  • Better Auth’s callback route is /api/auth/callback/:provider → for Google that’s /api/auth/callback/google.

5) Set env vars and restart

Add to tanstarter/.env:

GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...

Restart bun run dev after env changes.

Resend (optional: magic links + verification emails)

If you want email-based auth flows:

  1. Create an account: Resend
  2. Add a domain and verify it
  3. Create an API key
  4. Set:
RESEND_API_KEY=re_your-resend-api-key
RESEND_FROM_ADDRESS="Your App <noreply@yourdomain.com>"

Resend docs:

Note: if RESEND_* is missing, Google OAuth still works; email flows will not send.

Checking auth on the server

Prefer Better Auth’s server API in server routes (example: tanstarter/src/routes/api/subscription.ts):

const session = await auth.api.getSession({ headers: request.headers })
if (!session?.user) return new Response('Unauthorized', { status: 401 })

Tables created by Better Auth

With the Drizzle adapter, Better Auth stores data in:

  • user
  • session
  • account
  • verification