Skip to content
Better Auth: Integration Guide

Better Auth: Integration Guide Skill

The reference an agent needs to configure Better Auth correctly: adapters, session strategy, hooks, plugins, type safety and the gotchas that bite in production.

GitHub Docs

Install

Install with the skills CLI

One command for Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf and dozens of other agents. Needs Node.js; the skill is fetched straight from better-auth/skills.

$ npx skills add better-auth/skills --skill better-auth-best-practices

Run it in a terminal. The CLI finds the agents installed on your machine, asks which ones to add the skill to, and whether to install it into the folder you ran it from or for all your projects.

Other ways to install

Copy the whole skill folder — SKILL.md and any scripts or references beside it — into ~/.claude/skills/better-auth-best-practices/, where Claude Code picks it up in every project. Only git is needed.

tmp=$(mktemp -d) && \
  git clone --depth 1 --branch main https://github.com/better-auth/skills.git "$tmp" && \
  mkdir -p ~/.claude/skills && \
  cp -R "$tmp"/better-auth/best-practices ~/.claude/skills/better-auth-best-practices

No scripts or references next to it? Saving the SKILL.md alone as ~/.claude/skills/better-auth-best-practices/SKILL.md works too.

Using another agent? Its personal skills folder
Codex
~/.agents/skills/
Cursor
~/.cursor/skills/
GitHub Copilot
~/.copilot/skills/
Gemini CLI
~/.gemini/skills/
Windsurf
~/.codeium/windsurf/skills/
OpenCode
~/.config/opencode/skills/

About

  • Pins documentation lookups to the Better Auth version resolved from the lockfile
  • Uses the Better Auth MCP server for versioned doc search when it is available
  • Reference coverage of adapters, sessions, cookies, hooks, plugins and the client
  • Keeps upgrade guidance separate from installed-version guidance
  • Type-safety patterns that derive client types from the server instance
  • A Common Gotchas section for the failures that surface in production

This is Better Auth's own configuration reference, written for an agent rather than a reader. It is the skill that fires when someone mentions Better Auth, betterauth or auth.ts and wants the server and client set up properly — as opposed to scaffolded from scratch, which is the companion Create Auth skill's job.

Its most valuable habit is version discipline. Before answering anything the skill resolves which Better Auth release the project actually has — a version the user named, otherwise the resolved version in the lockfile, falling back to the package manifest — and then pins its documentation lookups to that line. Where the Better Auth MCP server is available it calls get_doc on /llms.txt to turn that version into a documentation identifier and passes it to every search; without MCP it starts from better-auth.com/llms.txt and follows the matching version index. It reaches for the latest docs only when the installed version genuinely cannot be determined or the user is asking about an upgrade, and when planning an upgrade it keeps guidance for the installed version separate from guidance for the target. That single rule removes the most common failure mode of LLM-written auth code: plausible config borrowed from a different major version.

The body covers the configuration surface in reference form — required environment variables and where auth.ts belongs, the CLI commands, core config options, database adapters, session management and cookie behaviour, user and account config, the email verification and reset flows, security settings, before/after hooks, the plugin system, the client, and how to derive types from the server instance so the client stays honest. A "Common Gotchas" section collects the mistakes that only show up under real traffic.

Published by the Better Auth team in their official skills repository, in the portable Agent Skills format.

SKILL.md

The instructions your agent loads, as published by Better Auth.

SKILL.md
name
better-auth-best-practices
description
Configure Better Auth server and client, set up database adapters, manage sessions, add plugins, and handle environment variables. Use when users mention Better Auth, betterauth, auth.ts, or need to set up TypeScript authentication with email/password, OAuth, or plugin configuration.

Better Auth Integration Guide

Documentation Version

Use documentation that matches the Better Auth version installed in the project. APIs and plugin names can differ across maintained release lines.

  1. Prefer a version explicitly named by the user.
  2. Otherwise, inspect the resolved better-auth version in the lockfile, falling back to the package manifest when no lockfile is available.
  3. When the Better Auth MCP is available, call get_doc with /llms.txt to resolve that package version to a documentation identifier. Pass the identifier to every search_docs call and pass result paths to get_doc unchanged.
  4. Without MCP, start at better-auth.com/llms.txt and follow the matching version index.
  5. Use the latest documentation only when the project version cannot be determined or the user explicitly asks about the latest release or an upgrade.

When planning an upgrade, separate guidance for the currently installed version from guidance for the target version.

---

Setup Workflow
  1. Install: npm install better-auth
  2. Set env vars: BETTER_AUTH_SECRET and BETTER_AUTH_URL
  3. Create auth.ts with database + config
  4. Create route handler for your framework
  5. Run migrations:
  • Built-in adapter: npx auth@latest migrate
  • Drizzle: npx auth@latest generate --output src/db/auth-schema.ts then npx drizzle-kit push (dev) or npx drizzle-kit generate && npx drizzle-kit migrate (prod)
  • Prisma: npx auth@latest generate --output prisma/schema.prisma then npx prisma migrate dev
  1. Verify: call GET /api/auth/ok — should return { status: "ok" }

---

Quick Reference
Environment Variables
  • BETTER_AUTH_SECRET - Encryption secret (min 32 chars). Generate: openssl rand -base64 32
  • BETTER_AUTH_URL - Base URL (e.g., https://example.com)

Only define baseURL/secret in config if env vars are NOT set.

File Location

CLI looks for auth.ts in: ./, ./lib, ./utils, or under ./src. Use --config for custom path.

CLI Commands
  • npx auth@latest migrate - Apply schema (built-in adapter)
  • npx auth@latest generate - Generate schema for Prisma/Drizzle
  • npx auth@latest mcp --cursor - Add MCP to AI tools

Re-run after adding/changing plugins.

---

Core Config Options
OptionNotes
appNameOptional display name
baseURLOnly if BETTER_AUTH_URL not set
basePathDefault /api/auth. Set / for root.
secretOnly if BETTER_AUTH_SECRET not set
databaseRequired for most features. See adapters docs.
secondaryStorageRedis/KV for sessions & rate limits
emailAndPassword{ enabled: true } to activate
socialProviders{ google: { clientId, clientSecret }, ... }
pluginsArray of plugins
trustedOriginsCSRF whitelist

---

Database

Direct connections: Pass pg.Pool, mysql2 pool, better-sqlite3, or bun:sqlite instance. For Postgres, also supports postgres (postgres.js) and @neondatabase/serverless.

ORM adapters: Import from better-auth/adapters/drizzle, better-auth/adapters/prisma, better-auth/adapters/mongodb.

Drizzle provider values: "pg" (PostgreSQL), "mysql" (MySQL), "sqlite" (SQLite). Must match the driver used.

Critical: Better Auth uses adapter model names, NOT underlying table names. If Prisma model is User mapping to table users, use modelName: "user" (Prisma reference), not "users".

---

Session Management

Storage priority:

  1. If secondaryStorage defined → sessions go there (not DB)
  2. Set session.storeSessionInDatabase: true to also persist to DB
  3. No database + cookieCache → fully stateless mode

Cookie cache strategies:

  • compact (default) - Base64url + HMAC. Smallest.
  • jwt - Standard JWT. Readable but signed.
  • jwe - Encrypted. Maximum security.

Key options: session.expiresIn (default 7 days), session.updateAge (refresh interval), session.cookieCache.maxAge, session.cookieCache.version (change to invalidate all sessions).

---

User & Account Config

User: user.modelName, user.fields (column mapping), user.additionalFields, user.changeEmail.enabled (disabled by default), user.deleteUser.enabled (disabled by default).

Account: account.modelName, account.accountLinking.enabled, account.storeAccountCookie (for stateless OAuth).

Required for registration: email and name fields.

---

Email Flows
  • emailVerification.sendVerificationEmail - Must be defined for verification to work
  • emailVerification.sendOnSignUp / sendOnSignIn - Auto-send triggers
  • emailAndPassword.sendResetPassword - Password reset email handler

---

Security

In `advanced`:

  • useSecureCookies - Force HTTPS cookies
  • disableCSRFCheck - ⚠️ Security risk
  • disableOriginCheck - ⚠️ Security risk
  • crossSubDomainCookies.enabled - Share cookies across subdomains
  • ipAddress.ipAddressHeaders - Custom IP headers for proxies
  • database.generateId - Custom ID generation or "serial"/"uuid"/false

Rate limiting: rateLimit.enabled, rateLimit.window, rateLimit.max, rateLimit.storage ("memory" | "database" | "secondary-storage").

---

Hooks

Endpoint hooks: hooks.before / hooks.after - Array of { matcher, handler }. Use createAuthMiddleware. Access ctx.path, ctx.context.returned (after), ctx.context.session.

Database hooks: databaseHooks.user.create.before/after, same for session, account. Useful for adding default values or post-creation actions.

Hook context (`ctx.context`): session, secret, authCookies, password.hash()/verify(), adapter, internalAdapter, generateId(), tables, baseURL.

---

Plugins

Import from dedicated paths for tree-shaking:

import { twoFactor } from "better-auth/plugins/two-factor"

NOT from "better-auth/plugins".

Popular plugins: twoFactor, organization, passkey, magicLink, emailOtp, username, phoneNumber, admin, apiKey, bearer, jwt, multiSession, sso, oauthProvider, oidcProvider, openAPI, genericOAuth.

Client plugins go in createAuthClient({ plugins: [...] }).

---

Client

Import from: better-auth/client (vanilla), better-auth/react, better-auth/vue, better-auth/svelte, better-auth/solid.

Key methods: signUp.email(), signIn.email(), signIn.social(), signOut(), useSession(), getSession(), revokeSession(), revokeSessions().

---

Type Safety

Infer types: typeof auth.$Infer.Session, typeof auth.$Infer.Session.user.

For separate client/server projects: createAuthClient<typeof auth>().

---

Common Gotchas
  1. Model vs table name - Config uses ORM model name, not DB table name
  2. Plugin schema - Re-run CLI after adding plugins
  3. Secondary storage - Sessions go there by default, not DB
  4. Cookie cache - Custom session fields NOT cached, always re-fetched
  5. Stateless mode - No DB = session in cookie only, logout on cache expiry
  6. Change email flow - Sends to current email first, then new email
  7. Drizzle: db not initialized - drizzleAdapter(db, ...) requires a db instance from drizzle(). See create-auth skill for setup examples (node-postgres, postgres.js, Neon).
  8. Drizzle: missing drizzle.config.ts - drizzle-kit commands require a drizzle.config.ts pointing to the generated schema file and DB credentials.

---

Resources
Install

Scaffold authentication in a TypeScript or JavaScript app with Better Auth — the skill detects the framework, ORM and package manager first, then plans before writing code.

Browse all skills →