Claude Code for Better Auth — Workflow Guide
The Setup
You are implementing authentication with Better Auth, the TypeScript-first auth library that provides email/password, OAuth, magic links, and more with a database-backed approach. Unlike managed services, Better Auth runs in your application and stores data in your database. Claude Code can implement Better Auth, but it generates NextAuth or Clerk patterns that differ significantly.
What Claude Code Gets Wrong By Default
-
Uses NextAuth’s provider-based config. Claude writes
providers: [GoogleProvider({...})]arrays. Better Auth configures providers differently through itsauth()function withsocialProvidersoption. -
Creates Clerk-style hosted UI components. Claude adds pre-built auth components. Better Auth is headless — you build your own UI and call Better Auth’s client API methods for authentication operations.
-
Stores sessions in JWT. Claude creates stateless JWT tokens. Better Auth uses database-backed sessions by default, giving you server-side session management with revocation support.
-
Ignores the database adapter setup. Claude uses Better Auth without configuring a database adapter. Better Auth requires a database (Drizzle, Prisma, or Kysely adapter) to store users, sessions, and accounts.
The CLAUDE.md Configuration
# Better Auth Project
## Auth
- Library: Better Auth (better-auth package)
- Database: Your own DB via adapter (Drizzle/Prisma/Kysely)
- Sessions: Database-backed, not JWT
- UI: Headless — build your own forms
## Better Auth Rules
- Server: const auth = betterAuth({ database, socialProviders, ... })
- Client: const authClient = createAuthClient({ baseURL })
- Sign up: authClient.signUp.email({ email, password, name })
- Sign in: authClient.signIn.email({ email, password })
- OAuth: authClient.signIn.social({ provider: 'google' })
- Session: authClient.getSession() on client, auth.api.getSession() on server
- Middleware: auth.handler for API route handling
- Plugins: two-factor, organization, admin via plugin system
## Conventions
- Auth config in lib/auth.ts (server)
- Auth client in lib/auth-client.ts (client)
- API route: /api/auth/[...all] catches all auth routes
- Database tables auto-created by Better Auth
- Session validation in middleware or server components
- Plugins added via plugins: [twoFactor(), organization()]
- Custom UI: forms call authClient methods directly
Workflow Example
You want to set up email auth with Google OAuth. Prompt Claude Code:
“Integrate Better Auth with email/password and Google OAuth sign-in. Configure the Drizzle database adapter, set up the API route handler, create the auth client, and build a login form component.”
Claude Code should create auth() config with Drizzle adapter and Google social provider, set up the catch-all API route, create the client with createAuthClient(), and build a form that calls authClient.signIn.email() and authClient.signIn.social({ provider: 'google' }).
Common Pitfalls
-
Database tables not migrated. Claude configures Better Auth but forgets to run migrations. Better Auth needs specific tables (user, session, account, verification). Run
npx better-auth migrateor let the library auto-create tables on first run. -
Client baseURL mismatch. Claude creates the auth client without specifying
baseURL, or hardcodeslocalhost. ThebaseURLmust match where the auth API route is hosted — use an environment variable that changes per environment. -
Missing CSRF protection configuration. Claude uses the default settings without considering CSRF. Better Auth includes CSRF protection, but custom API routes that interact with auth must include the CSRF token from
authClient.getSession().