better-auth
Skill for integrating Better Auth - comprehensive TypeScript authentication framework for Cloudflare D1, Next.js, Nuxt, and 15+ frameworks. Use when adding auth, encountering D1 adapter errors, or implementing OAuth/2FA/RBAC features.
What this skill does
# better-auth **Status**: Production Ready **Last Updated**: 2026-04-08 **Package**: `[email protected]` (ESM-only) **Dependencies**: Drizzle ORM or Kysely (required for D1 complex use cases; D1 native support available in v1.5+) --- ## Quick Start (5 Minutes) ### Installation **Option 1: Drizzle ORM (Recommended)** ```bash bun add better-auth drizzle-orm drizzle-kit ``` **Option 2: Kysely** ```bash bun add better-auth kysely @noxharmonium/kysely-d1 ``` ### ⚠️ v1.4.0+ Requirements better-auth v1.4.0+ is **ESM-only**. Ensure: **package.json**: ```json { "type": "module" } ``` **Upgrading from v1.3.x?** Load `references/migration-guide-1.4.0.md` **Upgrading from v1.4.x?** Load `references/migration-guide-1.5.0.md` ### ⚠️ CRITICAL: D1 Adapter Requirements **v1.5.0+**: D1 is now natively supported. Pass your D1 binding directly: ```typescript // ✅ SIMPLEST - D1 native (v1.5.0+) import { betterAuth } from "better-auth"; const auth = betterAuth({ database: env.DB, // D1 binding, auto-detected }); ``` For complex schemas, use Drizzle ORM: ```typescript // ✅ RECOMMENDED for complex schemas - Drizzle import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { drizzle } from "drizzle-orm/d1"; const auth = betterAuth({ database: drizzleAdapter(drizzle(env.DB, { schema }), { provider: "sqlite" }), }); ``` ```typescript // ❌ WRONG - This doesn't exist import { d1Adapter } from 'better-auth/adapters/d1' ``` ### Minimal Setup (Cloudflare Workers + Drizzle) **1. Create D1 Database:** ```bash wrangler d1 create my-app-db ``` **2. Define Schema** (`src/db/schema.ts`): ```typescript import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"; export const user = sqliteTable("user", { id: text().primaryKey(), name: text().notNull(), email: text().notNull().unique(), emailVerified: integer({ mode: "boolean" }).notNull().default(false), image: text(), }); export const session = sqliteTable("session", { id: text().primaryKey(), userId: text().notNull().references(() => user.id, { onDelete: "cascade" }), token: text().notNull(), expiresAt: integer({ mode: "timestamp" }).notNull(), }); // See references/database-schema.ts for complete schema ``` **3. Initialize Auth** (`src/auth.ts`): ```typescript import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { drizzle } from "drizzle-orm/d1"; import * as schema from "./db/schema"; export function createAuth(env: { DB: D1Database; BETTER_AUTH_SECRET: string }) { const db = drizzle(env.DB, { schema }); return betterAuth({ baseURL: env.BETTER_AUTH_URL, secret: env.BETTER_AUTH_SECRET, database: drizzleAdapter(db, { provider: "sqlite" }), emailAndPassword: { enabled: true }, }); } ``` **4. Create Worker** (`src/index.ts`): ```typescript import { Hono } from "hono"; import { createAuth } from "./auth"; const app = new Hono<{ Bindings: Env }>(); app.all("/api/auth/*", async (c) => { const auth = createAuth(c.env); return auth.handler(c.req.raw); }); export default app; ``` **5. Deploy:** ```bash bunx drizzle-kit generate wrangler d1 migrations apply my-app-db --remote wrangler deploy ``` --- ## Decision Tree **For code examples and syntax, always consult [better-auth.com/docs](https://better-auth.com/docs).** ``` Is this a new/empty project? ├─ YES → New project setup │ 1. Identify framework (Next.js, Nuxt, Workers, etc.) │ 2. Choose database (D1, PostgreSQL, MongoDB, MySQL) │ 3. Install better-auth + Drizzle/Kysely │ 4. Create auth.ts + auth-client.ts │ 5. Set up route handler (see Quick Start above) │ 6. Run migrations (Drizzle Kit for D1) │ 7. Add features via plugins (2FA, organizations, etc.) │ └─ NO → Does project have existing auth? ├─ YES → Migration/enhancement │ • Audit current auth for gaps │ • Plan incremental migration │ • See references/framework-comparison.md for migration guides │ └─ NO → Add auth to existing project 1. Analyze project structure 2. Install better-auth + adapter 3. Create auth config (see Quick Start) 4. Add route handler to existing routes 5. Run schema migrations 6. Integrate into existing pages/components ``` --- ## Critical Rules ### MUST DO ✅ Use `better-auth/minimal` + adapter packages for smallest bundle (v1.5+) ✅ Use `npx auth migrate` and `npx auth generate` for CLI commands (v1.5+) ✅ Set BETTER_AUTH_SECRET via `wrangler secret put` ✅ Configure CORS with `credentials: true` ✅ Match OAuth callback URLs exactly (no trailing slash) ✅ Apply migrations to local D1 before `wrangler dev` ✅ Use camelCase column names in schema ### NEVER DO ❌ Use `d1Adapter` (doesn't exist) ❌ Forget CORS credentials or mismatch OAuth URLs ❌ Use snake_case columns without CamelCasePlugin ❌ Skip local migrations or hardcode secrets ❌ Leave sendVerificationEmail unimplemented ### ⚠️ v1.5.0 Breaking Changes **API Key Plugin Moved**: ```typescript - import { apiKey } from "better-auth/plugins"; + import { apiKey } from "@better-auth/api-key"; ``` Schema: `userId` → `referenceId`, new `configId` field. **After Hooks**: Database after-hooks now run post-transaction (not inside it). **Deprecated APIs Removed**: `Adapter` → `DBAdapter`, `InferUser`/`InferSession` removed, `@better-auth/core/utils` split into subpath exports. **Load `references/migration-guide-1.5.0.md` when upgrading from <1.5.0** ### ⚠️ v1.6.0 Breaking Changes **Session Freshness**: `freshAge` now uses `createdAt` (not `updatedAt`). Sessions may require re-auth more frequently for sensitive operations. **SAML Security**: InResponseTo validation is **default ON**. Opt out with `saml: { enableInResponseToValidation: false }`. **OIDC Provider Deprecated**: Use `@better-auth/oauth-provider` instead. ### New in v1.5.0 (Highlights) - **New CLI**: `npx auth init/migrate/generate/upgrade` - **D1 Native**: Pass D1 binding directly (no adapter needed) - **OAuth 2.1 Provider**: `@better-auth/oauth-provider` (MCP-ready) - **Electron**: `@better-auth/electron` for desktop apps - **i18n**: `@better-auth/i18n` for error translations - **Dynamic Base URL**: Multi-domain/preview deployment support - **Secret Key Rotation**: Non-destructive, versioned secrets - **Test Utils**: Factories, OTP capture, login helpers - **Typed Error Codes**: Machine-readable `code` in error responses **Load `references/v1.5-features.md` for detailed implementation guides.** ### New in v1.6.0 (Highlights) - **OpenTelemetry**: Distributed tracing (experimental) - **Passkey Pre-Auth**: Register passkeys before session - **Non-blocking Scrypt**: Password hashing on libuv thread pool - **46% Smaller Package**: 4.2MB → 2.3MB - **Case Insensitive Queries**: `mode: "insensitive"` on adapter queries **Load `references/v1.6-features.md` for detailed implementation guides.** --- ## Quick Reference ### Environment Variables | Variable | Purpose | Example | |----------|---------|---------| | `BETTER_AUTH_SECRET` | Encryption secret (min 32 chars) | Generate: `openssl rand -base64 32` | | `BETTER_AUTH_URL` | Base URL | `https://example.com` or `http://localhost:8787` | | `DATABASE_URL` | Database connection (optional for D1) | PostgreSQL/MySQL connection string | **Note**: Only define `baseURL`/`secret` in config if env vars are NOT set. ### CLI Commands (v1.5+) | Command | Purpose | |---------|---------| | `npx auth init` | Interactive project scaffolding | | `npx auth migrate` | Run database migrations | | `npx auth generate` | Generate auth schema | | `npx auth generate --adapter drizzle` | Adapter-specific schema | | `npx auth upgrade` | Upgrade to latest version | | `bunx drizzle-kit generate` | **D1: Use this** to generate Drizzle migrations | | `wrangler d1 migrations apply DB_NAME` | **D1: Use this** to apply migrations | **Re-run after adding/changing plugins.** ### Core Config Options | Option | Notes | |--------|----
Related in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.