better-auth-complete-guide
Better Auth Complete Guide
Better Auth is a framework-agnostic authentication (and authorization) library for TypeScript. It provides comprehensive features out of the box with a plugin ecosystem for advanced functionalities like 2FA, multi-tenant support, passkeys, and more.
When to Apply
Reference these guidelines when:
- Setting up Better Auth in a new or existing project
- Configuring authentication methods (email/password, social OAuth, magic link, passkey, etc.)
- Integrating with a framework (Next.js, Nuxt, SvelteKit, Astro, Hono, Express, Convex, etc.)
- Setting up database adapters (Prisma, Drizzle, MongoDB, raw SQL)
- Implementing session management, cookie caching, or secondary storage
- Adding plugins (2FA, organization, admin, API keys, anonymous auth, etc.)
- Building client-side auth UI with React, Vue, Svelte, Solid, or vanilla JS
- Securing API routes with middleware and rate limiting
- Running CLI commands (migrate, generate)
Installation & Setup
1. Install
npm install better-auth # or pnpm add / yarn add / bun add
If using separate client/server, install in both.
2. Environment Variables
BETTER_AUTH_SECRET= # min 32 chars, generate with: openssl rand -base64 32
BETTER_AUTH_URL=http://localhost:3000 # Base URL of your app
3. Create Auth Instance
Create auth.ts (or lib/auth.ts, src/lib/auth.ts, etc.):
import { betterAuth } from "better-auth";
export const auth = betterAuth({
database: /* see Database section */,
emailAndPassword: { enabled: true },
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
},
});
4. Database Configuration
Direct connections (uses built-in Kysely adapter):
// SQLite
import Database from "better-sqlite3";
database: new Database("./sqlite.db")
// PostgreSQL
import { Pool } from "pg";
database: new Pool({ connectionString: process.env.DATABASE_URL })
// MySQL
import { createPool } from "mysql2/promise";
database: createPool({ uri: process.env.DATABASE_URL })
ORM adapters:
// Drizzle
import { drizzleAdapter } from "better-auth/adapters/drizzle";
database: drizzleAdapter(db, { provider: "pg" }) // "pg" | "mysql" | "sqlite"
// Prisma
import { prismaAdapter } from "better-auth/adapters/prisma";
database: prismaAdapter(prisma, { provider: "postgresql" })
// MongoDB
import { mongodbAdapter } from "better-auth/adapters/mongodb";
database: mongodbAdapter(client)
5. Create Database Tables
npx auth@latest generate # Generate ORM schema or SQL migration
npx auth@latest migrate # Apply migration directly (Kysely adapter only)
6. Mount Route Handler
See references/framework-integrations.md for all frameworks.
Next.js App Router (most common):
// app/api/auth/[...all]/route.ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";
export const { POST, GET } = toNextJsHandler(auth);
7. Create Client
// lib/auth-client.ts
import { createAuthClient } from "better-auth/react"; // or /vue, /svelte, /solid, /client
export const authClient = createAuthClient({
baseURL: "http://localhost:3000", // optional if same domain
});
// Can also destructure: export const { signIn, signUp, useSession } = createAuthClient();
Core Authentication
Email & Password
Client - Sign Up:
const { data, error } = await authClient.signUp.email({
email, password, name,
image: "optional-url",
callbackURL: "/dashboard",
}, {
onSuccess: (ctx) => { /* redirect */ },
onError: (ctx) => { alert(ctx.error.message) },
});
Client - Sign In:
const { data, error } = await authClient.signIn.email({
email, password,
callbackURL: "/dashboard",
rememberMe: true, // default: true
});
Server - Sign In:
const response = await auth.api.signInEmail({
body: { email, password },
asResponse: true, // returns Response object
});
Social Sign-On
// Client
await authClient.signIn.social({
provider: "github", // "google", "apple", "discord", etc.
callbackURL: "/dashboard",
});
Supported providers: Google, GitHub, Apple, Discord, Microsoft, Facebook, Twitter/X, LinkedIn, Spotify, Twitch, GitLab, Slack, Reddit, TikTok, Dropbox, Zoom, Figma, Roblox, Notion, Linear, Atlassian, Salesforce, PayPal, VK, Kakao, Naver, LINE, HuggingFace, Kick, Polar, Vercel, Railway, Cognito, Paybin.
Session Management
Client - Get Session (React hook):
const { data: session, isPending, error, refetch } = authClient.useSession();
// session.user, session.session
Server - Get Session:
const session = await auth.api.getSession({ headers: req.headers });
Sign Out:
await authClient.signOut();
// Or revoke all sessions:
await authClient.signOut({ fetchOptions: { onSuccess: () => redirect("/") } });
Configuration Options
Key betterAuth({...}) options:
| Option | Description | Default |
|---|---|---|
appName |
Application name | "Better Auth" |
baseURL |
Server base URL (or BETTER_AUTH_URL env) |
auto-detect |
basePath |
Auth API path | "/api/auth" |
secret |
Encryption secret (or BETTER_AUTH_SECRET env) |
required in prod |
database |
Database connection or adapter | required |
emailAndPassword.enabled |
Enable email/password auth | false |
socialProviders |
OAuth provider configs | {} |
plugins |
Array of plugins | [] |
session.expiresIn |
Session TTL in seconds | 7 days |
session.updateAge |
Session refresh interval | 1 day |
session.cookieCache.enabled |
Cache session in cookie | false |
trustedOrigins |
Allowed origins for CSRF | [] |
rateLimit.window |
Rate limit window (seconds) | 10 |
rateLimit.max |
Max requests per window | 100 |
Advanced options: advanced.useSecureCookies, advanced.disableCSRFCheck,
advanced.crossSubDomainCookies, advanced.cookiePrefix, advanced.backgroundTasks.
Plugins
Plugins extend functionality. Add to server AND client:
// Server (auth.ts)
import { twoFactor } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [twoFactor()],
});
// Client (auth-client.ts)
import { twoFactorClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
plugins: [twoFactorClient()],
});
Available built-in plugins:
| Plugin | Import | Description |
|---|---|---|
| Two Factor | twoFactor |
TOTP, SMS, backup codes |
| Organization | organization |
Multi-tenant, teams, RBAC |
| Admin | admin |
User management, impersonation |
| API Key | apiKey |
API key authentication |
| Username | username |
Username-based auth |
| Magic Link | magicLink |
Email magic link auth |
| Email OTP | emailOtp |
One-time password via email |
| Phone Number | phoneNumber |
Phone/SMS auth |
| Passkey | passkey |
WebAuthn/FIDO2 |
| Anonymous | anonymous |
Guest/anonymous users |
| Bearer | bearer |
Bearer token auth |
| JWT | jwt |
JWT session tokens |
| Multi Session | multiSession |
Multiple active sessions |
| One Tap | oneTap |
Google One Tap |
| OIDC Provider | oidcProvider |
Act as OIDC provider |
| Open API | openAPI |
OpenAPI spec generation |
| OAuth Proxy | oauthProxy |
Proxy OAuth callbacks |
| Custom Session | customSession |
Extend session data |
| Access / RBAC | access |
Role-based access control |
| HaveIBeenPwned | haveibeenpwned |
Password breach check |
| SIWE | siwe |
Sign-In With Ethereum |
| Captcha | captcha |
reCAPTCHA/hCaptcha/Turnstile |
| Last Login Method | lastLoginMethod |
Track login method |
| MCP | mcp |
Model Context Protocol auth |
See references/plugins.md for detailed plugin configuration.
Database Hooks
export const auth = betterAuth({
databaseHooks: {
user: {
create: {
before: async (user, ctx) => {
// Return false to cancel, or { data: modifiedUser } to modify
},
after: async (user, ctx) => { /* post-creation logic */ },
},
update: { before: async (user, ctx) => {}, after: async (user, ctx) => {} },
delete: { before: async (user, ctx) => {}, after: async (user, ctx) => {} },
},
session: { create: {}, update: {}, delete: {} },
account: { create: {}, update: {}, delete: {} },
verification: { create: {}, update: {}, delete: {} },
},
});
Request Hooks (Middleware)
export const auth = betterAuth({
hooks: {
before: async (ctx) => {
// Runs before every auth request. Modify ctx or throw APIError.
},
after: async (ctx) => {
// Runs after every auth request.
},
},
});
Core Database Schema
Better Auth requires these tables (auto-created by CLI):
- user: id, name, email, emailVerified, image, createdAt, updatedAt
- session: id, expiresAt, token, createdAt, updatedAt, ipAddress, userAgent, userId
- account: id, accountId, providerId, userId, accessToken, refreshToken, idToken, accessTokenExpiresAt, refreshTokenExpiresAt, scope, password, createdAt, updatedAt
- verification: id, identifier, value, expiresAt, createdAt, updatedAt
Plugins may add additional tables/fields. Run npx auth@latest generate after adding plugins.
Key Patterns
- Always export auth instance as
author default export - CLI auto-detects it - Client methods are for client-side only - Use
auth.api.*on server - Add client plugins matching server plugins - e.g.,
twoFactor()server +twoFactorClient()client - Set
trustedOriginsfor cross-origin setups - Run
npx auth@latest generateafter adding/removing plugins to update schema - Use
asResponse: trueinauth.api.*calls when you need to return a Response - Mount handler on
/api/auth/*catch-all route (configurable viabasePath)
Reference Files
For detailed framework-specific and plugin-specific guides: