skills/fernando-augustop/better-auth-skill/better-auth-complete-guide

better-auth-complete-guide

SKILL.md

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, 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

  1. Always export auth instance as auth or default export - CLI auto-detects it
  2. Client methods are for client-side only - Use auth.api.* on server
  3. Add client plugins matching server plugins - e.g., twoFactor() server + twoFactorClient() client
  4. Set trustedOrigins for cross-origin setups
  5. Run npx auth@latest generate after adding/removing plugins to update schema
  6. Use asResponse: true in auth.api.* calls when you need to return a Response
  7. Mount handler on /api/auth/* catch-all route (configurable via basePath)

Reference Files

For detailed framework-specific and plugin-specific guides:

Weekly Installs
2
First Seen
Feb 24, 2026
Installed on
mcpjam2
claude-code2
replit2
junie2
windsurf2
zencoder2