Better Auth Architecture & Implementation Guide
Better Auth is a TypeScript-first, framework-agnostic authentication and authorization library with built-in support for social OAuth, credentials, multi-factor authentication, multi-tenant organizations, and database adapters.
1. Mental Model & Architecture
Better Auth consists of two interconnected instances:
┌──────────────────────────────────────┐ ┌──────────────────────────────────────┐
│ Better Auth Server API │ │ Better Auth Web Client │
│ (betterAuth({ database, ... })) │ ◄───► │ (createAuthClient({ ... })) │
│ - Database Adapter (Prisma/Mongo) │ HTTP │ - React hooks (useSession) │
│ - Session & Cookie Signing │ REST │ - signIn, signUp, signOut methods │
│ - Plugins (2FA, Organization, etc.) │ │ - Client plugins (organization, 2FA)│
└──────────────────────────────────────┘ └──────────────────────────────────────┘
- Server (
auth.ts): Created with betterAuth({ ... }). Mounts on an API endpoint (/api/auth/*) in your backend or framework (e.g. Next.js App Router, Express, SvelteKit, Hono). Manages schema migrations, database sessions, password hashing, and token signing.
- Client (
auth-client.ts): Created with createAuthClient({ baseURL }). Provides reactive hooks (useSession()) and typed methods (authClient.signIn.email(), authClient.signUp.email(), authClient.signIn.social()).
- Database Layer: Persists
User, Session, Account, Verification, plus plugin-specific tables (Organization, Member, Invitation, TwoFactor). Managed automatically with the CLI:
npx @better-auth/cli@latest migrate (built-in adapters)
npx @better-auth/cli@latest generate (Prisma / Drizzle)
2. Fast Setup Workflow
Step 1: Environment Variables
Generate a 32-character high-entropy secret:
# Terminal
openssl rand -base64 32
Add to .env:
BETTER_AUTH_SECRET="your-32-char-random-secret"
BETTER_AUTH_URL="http://localhost:3000" # Base URL of the app
Step 2: Server Configuration (auth.ts)
import { betterAuth } from "better-auth";
import { mongodbAdapter } from "better-auth/adapters/mongodb"; // or prismaAdapter, drizzleAdapter
import { client } from "@/lib/db";
export const auth = betterAuth({
database: mongodbAdapter(client.db()), // or database: { provider: "sqlite", url: "..." }
secret: process.env.BETTER_AUTH_SECRET,
baseURL: process.env.BETTER_AUTH_URL,
emailAndPassword: {
enabled: true,
},
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
},
});
Step 3: Route Handlers
- Next.js App Router (
app/api/auth/[...all]/route.ts):import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";
export const { GET, POST } = toNextJsHandler(auth);
- Express / Node (
src/server.ts):import { toNodeHandler } from "better-auth/node";
app.all("/api/auth/*", toNodeHandler(auth));
Step 4: Client Setup (auth-client.ts)
import { createAuthClient } from "better-auth/react"; // or better-auth/client for vanilla JS
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000",
});
export const { useSession, signIn, signUp, signOut } = authClient;
Step 5: Database Migration & Verification
npx @better-auth/cli@latest migrate
Verify the server is running by hitting GET /api/auth/ok (returns { status: "ok" }).
3. Detailed Technical References
Better Auth contains specialized modules for complex auth flows. Consult the dedicated reference guides below as needed:
| Feature / Domain |
Reference Document |
Description |
| Project Planning & Scaffolding |
📖 scaffolding-and-setup.md |
Framework detection, ORM selection, initial questionnaire, and UI scaffold generation. |
| Core Configuration & Adapters |
📖 configuration.md |
Database adapters (Prisma, Drizzle, MongoDB, Kysely), secondary storage (Redis), and CLI workflows. |
| Security & Hardening |
📖 security-and-hardening.md |
Rate limiting, secret entropy, CSRF protection, trusted origins, cookie options, and audit logs. |
| Email & Password Authentication |
📖 email-and-password.md |
Email verification, password reset tokens, password policies, and custom argon2/bcrypt hashing. |
| Organizations & Multi-Tenancy |
📖 organizations-and-teams.md |
Organization creation, member invitations, custom RBAC permissions, teams, and active org switching. |
| Two-Factor Authentication (2FA) |
📖 two-factor-authentication.md |
TOTP authenticator apps (QR codes), backup codes, SMS/email OTPs, and trusted devices. |
4. Key Implementation Rules
- Secret Validation:
BETTER_AUTH_SECRET must be at least 32 characters long. Better Auth will refuse to boot with weak or placeholder keys in production.
- Re-Run CLI on Plugin Changes: Whenever you add a plugin (e.g.
organization(), twoFactor()), always run npx @better-auth/cli migrate (or generate) to create the new tables/collections.
- Keep Server & Client Plugins in Sync: If you enable a plugin on the server (e.g.
plugins: [organization()]), you must also enable its counterpart on the client (e.g. plugins: [organizationClient()]), otherwise client methods will be missing from TypeScript definitions.
- Environment Variables Over Config: Define
BETTER_AUTH_SECRET and BETTER_AUTH_URL in .env rather than hardcoding secret and baseURL in code.
1---2name: better-auth3description: Complete authentication architecture and implementation guide for Better Auth across TypeScript/JavaScript applications (Next.js, Express, SvelteKit, Nuxt, Astro, Hono). Make sure to use this skill whenever the user mentions Better Auth, betterauth, auth.ts, session management, OAuth/social login (Google, GitHub, Apple), email/password auth, two-factor authentication (2FA/MFA), organizations/teams, multi-tenancy, rate limiting, or database adapters (Prisma, Drizzle, MongoDB/Mongoose, SQLite, PostgreSQL), even if they only ask to "add login" or "set up authentication".4---56# Better Auth Architecture & Implementation Guide78Better Auth is a TypeScript-first, framework-agnostic authentication and authorization library with built-in support for social OAuth, credentials, multi-factor authentication, multi-tenant organizations, and database adapters.910---1112## 1. Mental Model & Architecture1314Better Auth consists of two interconnected instances:1516```17┌──────────────────────────────────────┐ ┌──────────────────────────────────────┐18│ Better Auth Server API │ │ Better Auth Web Client │19│ (betterAuth({ database, ... })) │ ◄───► │ (createAuthClient({ ... })) │20│ - Database Adapter (Prisma/Mongo) │ HTTP │ - React hooks (useSession) │21│ - Session & Cookie Signing │ REST │ - signIn, signUp, signOut methods │22│ - Plugins (2FA, Organization, etc.) │ │ - Client plugins (organization, 2FA)│23└──────────────────────────────────────┘ └──────────────────────────────────────┘24```25261. **Server (`auth.ts`):** Created with `betterAuth({ ... })`. Mounts on an API endpoint (`/api/auth/*`) in your backend or framework (e.g. Next.js App Router, Express, SvelteKit, Hono). Manages schema migrations, database sessions, password hashing, and token signing.272. **Client (`auth-client.ts`):** Created with `createAuthClient({ baseURL })`. Provides reactive hooks (`useSession()`) and typed methods (`authClient.signIn.email()`, `authClient.signUp.email()`, `authClient.signIn.social()`).283. **Database Layer:** Persists `User`, `Session`, `Account`, `Verification`, plus plugin-specific tables (`Organization`, `Member`, `Invitation`, `TwoFactor`). Managed automatically with the CLI:29 - `npx @better-auth/cli@latest migrate` (built-in adapters)30 - `npx @better-auth/cli@latest generate` (Prisma / Drizzle)3132---3334## 2. Fast Setup Workflow3536### Step 1: Environment Variables37Generate a 32-character high-entropy secret:38```bash39# Terminal40openssl rand -base64 3241```42Add to `.env`:43```env44BETTER_AUTH_SECRET="your-32-char-random-secret"45BETTER_AUTH_URL="http://localhost:3000" # Base URL of the app46```4748### Step 2: Server Configuration (`auth.ts`)49```ts50import { betterAuth } from "better-auth";51import { mongodbAdapter } from "better-auth/adapters/mongodb"; // or prismaAdapter, drizzleAdapter52import { client } from "@/lib/db";5354export const auth = betterAuth({55 database: mongodbAdapter(client.db()), // or database: { provider: "sqlite", url: "..." }56 secret: process.env.BETTER_AUTH_SECRET,57 baseURL: process.env.BETTER_AUTH_URL,58 emailAndPassword: {59 enabled: true,60 },61 socialProviders: {62 github: {63 clientId: process.env.GITHUB_CLIENT_ID!,64 clientSecret: process.env.GITHUB_CLIENT_SECRET!,65 },66 google: {67 clientId: process.env.GOOGLE_CLIENT_ID!,68 clientSecret: process.env.GOOGLE_CLIENT_SECRET!,69 },70 },71});72```7374### Step 3: Route Handlers75- **Next.js App Router (`app/api/auth/[...all]/route.ts`):**76 ```ts77 import { auth } from "@/lib/auth";78 import { toNextJsHandler } from "better-auth/next-js";7980 export const { GET, POST } = toNextJsHandler(auth);81 ```82- **Express / Node (`src/server.ts`):**83 ```ts84 import { toNodeHandler } from "better-auth/node";85 app.all("/api/auth/*", toNodeHandler(auth));86 ```8788### Step 4: Client Setup (`auth-client.ts`)89```ts90import { createAuthClient } from "better-auth/react"; // or better-auth/client for vanilla JS9192export const authClient = createAuthClient({93 baseURL: process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000",94});9596export const { useSession, signIn, signUp, signOut } = authClient;97```9899### Step 5: Database Migration & Verification100```bash101npx @better-auth/cli@latest migrate102```103Verify the server is running by hitting `GET /api/auth/ok` (returns `{ status: "ok" }`).104105---106107## 3. Detailed Technical References108109Better Auth contains specialized modules for complex auth flows. Consult the dedicated reference guides below as needed:110111| Feature / Domain | Reference Document | Description |112| --- | --- | --- |113| **Project Planning & Scaffolding** | 📖 [scaffolding-and-setup.md](references/scaffolding-and-setup.md) | Framework detection, ORM selection, initial questionnaire, and UI scaffold generation. |114| **Core Configuration & Adapters** | 📖 [configuration.md](references/configuration.md) | Database adapters (Prisma, Drizzle, MongoDB, Kysely), secondary storage (Redis), and CLI workflows. |115| **Security & Hardening** | 📖 [security-and-hardening.md](references/security-and-hardening.md) | Rate limiting, secret entropy, CSRF protection, trusted origins, cookie options, and audit logs. |116| **Email & Password Authentication** | 📖 [email-and-password.md](references/email-and-password.md) | Email verification, password reset tokens, password policies, and custom argon2/bcrypt hashing. |117| **Organizations & Multi-Tenancy** | 📖 [organizations-and-teams.md](references/organizations-and-teams.md) | Organization creation, member invitations, custom RBAC permissions, teams, and active org switching. |118| **Two-Factor Authentication (2FA)** | 📖 [two-factor-authentication.md](references/two-factor-authentication.md) | TOTP authenticator apps (QR codes), backup codes, SMS/email OTPs, and trusted devices. |119120---121122## 4. Key Implementation Rules1231241. **Secret Validation:** `BETTER_AUTH_SECRET` must be at least 32 characters long. Better Auth will refuse to boot with weak or placeholder keys in production.1252. **Re-Run CLI on Plugin Changes:** Whenever you add a plugin (e.g. `organization()`, `twoFactor()`), always run `npx @better-auth/cli migrate` (or `generate`) to create the new tables/collections.1263. **Keep Server & Client Plugins in Sync:** If you enable a plugin on the server (e.g. `plugins: [organization()]`), you **must** also enable its counterpart on the client (e.g. `plugins: [organizationClient()]`), otherwise client methods will be missing from TypeScript definitions.1274. **Environment Variables Over Config:** Define `BETTER_AUTH_SECRET` and `BETTER_AUTH_URL` in `.env` rather than hardcoding `secret` and `baseURL` in code.