better-auth
Original:🇺🇸 English
Translated
8 scripts
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.
2installs
Sourcesecondsky/claude-skills
Added on
NPX Install
npx skill4agent add secondsky/claude-skills better-authTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →better-auth
Status: Production Ready
Last Updated: 2025-12-26
Package: (ESM-only)
Dependencies: Drizzle ORM or Kysely (required for D1)
better-auth@1.4.9Quick Start (5 Minutes)
Installation
Option 1: Drizzle ORM (Recommended)
bash
bun add better-auth drizzle-orm drizzle-kitOption 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⚠️ CRITICAL: D1 Adapter Requirements
better-auth DOES NOT have a direct . You MUST use either:
d1Adapter()- Drizzle ORM (recommended) -
drizzleAdapter() - Kysely (alternative) - Kysely instance with D1Dialect
typescript
// ❌ WRONG - This doesn't exist
import { d1Adapter } from 'better-auth/adapters/d1'
// ✅ CORRECT - Use Drizzle
import { drizzleAdapter } from 'better-auth/adapters/drizzle'
import { drizzle } from 'drizzle-orm/d1'Minimal Setup (Cloudflare Workers + Drizzle)
1. Create D1 Database:
bash
wrangler d1 create my-app-db2. Define Schema ():
src/db/schema.tstypescript
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 schema3. Initialize Auth ():
src/auth.tstypescript
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.tstypescript
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 deployDecision Tree
For code examples and syntax, always consult 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/componentsCritical Rules
MUST DO
✅ Use Drizzle/Kysely adapter (d1Adapter doesn't exist)
✅ Use Drizzle Kit for migrations (not )
✅ Set BETTER_AUTH_SECRET via
✅ Configure CORS with
✅ Match OAuth callback URLs exactly (no trailing slash)
✅ Apply migrations to local D1 before
✅ Use camelCase column names in schema
better-auth migratewrangler secret putcredentials: truewrangler devNEVER DO
❌ Use or with D1
❌ Forget CORS credentials or mismatch OAuth URLs
❌ Use snake_case columns without CamelCasePlugin
❌ Skip local migrations or hardcode secrets
❌ Leave sendVerificationEmail unimplemented
d1Adapterbetter-auth migrate⚠️ v1.4.0+ Breaking Changes
ESM-only (no CommonJS):
json
// package.json required
{ "type": "module" }API Renames:
- →
forgetPasswordrequestPasswordReset - POST → GET
/account-info/account-info
Callback Signatures:
typescript
// v1.3.x: request parameter
sendVerificationEmail: async ({ user, url, request }) => {}
// v1.4.0+: ctx parameter
sendVerificationEmail: async ({ user, url, ctx }) => {}Load when upgrading from <1.4.0
references/migration-guide-1.4.0.mdNew in v1.4.4-1.4.8
Key additions since v1.4.3:
- Background Tasks (v1.4.8): Global config to defer email sending
backgroundTasks - New OAuth Providers: Patreon (v1.4.8), Vercel (v1.4.3), Kick (v1.4.6) with refresh tokens
- OAuth 2.1 Plugin (v1.4.8): Standards-compliant OAuth implementation
- CLI Tool (v1.4.4): CLI with project scaffolding
better-auth - SAML/SSO: Clock skew validation (v1.4.7), InResponseTo, OIDC discovery
- Admin Permissions (v1.4.7): Admin role with granular user update permissions
- ctx.isTrustedDomain (v1.4.6): Helper for domain verification
Load for detailed implementation guides.
references/v1.4-features.mdQuick Reference
Environment Variables
| Variable | Purpose | Example |
|---|---|---|
| Encryption secret (min 32 chars) | Generate: |
| Base URL | |
| Database connection (optional for D1) | PostgreSQL/MySQL connection string |
Note: Only define / in config if env vars are NOT set.
baseURLsecretCLI Commands
| Command | Purpose |
|---|---|
| Apply schema (built-in Kysely adapter only) |
| Generate schema for Prisma/Drizzle |
| D1: Use this to generate Drizzle migrations |
| D1: Use this to apply migrations |
Re-run after adding/changing plugins.
Core Config Options
| Option | Notes |
|---|---|
| Optional display name |
| Only if |
| Default |
| Only if |
| Required for most features. Use |
| Redis/KV for sessions & rate limits |
| |
| |
| Array of plugins (import from dedicated paths) |
| CSRF whitelist for cross-origin requests |
Common Plugins
Import from dedicated paths for tree-shaking:
typescript
import { twoFactor } from "better-auth/plugins/two-factor"
import { organization } from "better-auth/plugins/organization"
import { passkey } from "@better-auth/passkey" // Separate packageNOT .
from "better-auth/plugins"Top 5 Errors (See references/error-catalog.md for all 15)
Error #1: "d1Adapter is not exported"
Problem: Trying to use non-existent
Solution: Use or Kysely instead (see Quick Start above)
d1AdapterdrizzleAdapterError #2: Schema Generation Fails
Problem: doesn't work with D1
Solution: Use then
better-auth migratebunx drizzle-kit generatewrangler d1 migrations applyError #3: CamelCase vs snake_case Mismatch
Problem: Database uses but better-auth expects
Solution: Use camelCase in schema or add to Kysely
email_verifiedemailVerifiedCamelCasePluginError #4: CORS Errors
Problem: errors, cookies not sent
Solution: Configure CORS with and correct origins
Access-Control-Allow-Origincredentials: trueError #5: OAuth Redirect URI Mismatch
Problem: Social sign-in fails with "redirect_uri_mismatch"
Solution: Ensure exact match:
https://yourdomain.com/api/auth/callback/googleLoad for all 15 errors with detailed solutions.
references/error-catalog.mdCommon Use Cases
Use Case 1: Email/Password Authentication
When: Basic authentication without social providers
Quick Pattern:
typescript
// Client
await authClient.signIn.email({
email: "user@example.com",
password: "password123",
});
// Server - enable in config
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
}Load: → Step 5
references/setup-guide.mdUse Case 2: Social Authentication (45+ Providers)
When: Allow users to sign in with social accounts
Supported: Google, GitHub, Microsoft, Apple, Discord, TikTok, Twitch, Spotify, LinkedIn, Slack, Reddit, Facebook, Twitter/X, Patreon, Vercel, Kick, and 30+ more.
Quick Pattern:
typescript
// Client
await authClient.signIn.social({
provider: "google",
callbackURL: "/dashboard",
});
// Server config
socialProviders: {
google: {
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
scope: ["openid", "email", "profile"],
},
}Load: → Step 5
references/setup-guide.mdUse Case 3: Protected API Routes
When: Need to verify user is authenticated
Quick Pattern:
typescript
app.get("/api/protected", async (c) => {
const auth = createAuth(c.env);
const session = await auth.api.getSession({
headers: c.req.raw.headers,
});
if (!session) {
return c.json({ error: "Unauthorized" }, 401);
}
return c.json({ data: "protected", user: session.user });
});Load:
references/cloudflare-worker-drizzle.tsUse Case 4: Multi-Tenant with Organizations
When: Building SaaS with teams/organizations
Load: → Organizations & Teams
references/advanced-features.mdUse Case 5: Two-Factor Authentication
When: Need extra security with 2FA/TOTP
Load: → Two-Factor Authentication
references/advanced-features.mdWhen to Load References
Load when:
references/setup-guide.md- User needs complete 8-step setup walkthrough
- User asks about Kysely adapter alternative
- User needs help with migrations or deployment
- User asks about wrangler.toml configuration
Load when:
references/error-catalog.md- Encountering any of the 12 documented errors
- User reports D1 adapter, schema, CORS, or OAuth issues
- User asks about troubleshooting or debugging
- User needs prevention checklist
Load when:
references/advanced-features.md- User asks about 2FA, passkeys, or magic links
- User needs organizations, teams, or RBAC
- User asks about rate limiting or session management
- User wants migration guide from Clerk or Auth.js
- User needs security best practices or performance optimization
Load when:
references/cloudflare-worker-drizzle.ts- User needs complete Worker implementation example
- User asks for production-ready code
- User wants to see full auth flow with protected routes
Load when:
references/cloudflare-worker-kysely.ts- User prefers Kysely over Drizzle
- User asks for Kysely-specific implementation
Load when:
references/database-schema.ts- User needs complete better-auth schema with all tables
- User asks about custom tables or schema extension
- User needs TypeScript types for database
Load when:
references/react-client-hooks.tsx- User building React/Next.js frontend
- User needs login forms, session hooks, or protected routes
- User asks about client-side implementation
Load when:
references/configuration-guide.md- User asks about production configuration
- User needs environment variable setup or wrangler.toml
- User asks about session configuration or ESM setup
- User needs CORS configuration, rate limiting, or API keys
- User asks about troubleshooting configuration issues
Load when:
references/framework-comparison.md- User asks "better-auth vs Clerk" or "vs Auth.js"
- User needs help choosing auth framework
- User wants feature comparison, migration advice, or cost analysis
- User asks about v1.4.0+ new features (database joins, stateless sessions)
Load when:
references/migration-guide-1.4.0.md- User upgrading from better-auth <1.4.0 to 1.4.0+
- User encounters errors or ESM issues
forgetPassword - User asks about breaking changes or migration steps
- User needs to migrate callback functions or API endpoints
Load when:
references/v1.4-features.md- User asks about background tasks or deferred email sending
- User needs Patreon, Vercel, or Kick OAuth provider setup
- User asks about OAuth 2.1 compliance
- User needs SAML/SSO with clock skew or OIDC discovery
- User asks about the better-auth CLI tool
- User needs admin role permissions configuration
- User asks about ctx.isTrustedDomain or domain verification
Load when:
references/nextjs/README.md- User building Next.js app with PostgreSQL (not Cloudflare D1)
- User needs organizations and 2FA example
- User asks about Next.js-specific implementation
Load when:
references/nextjs/postgres-example.ts- User needs complete Next.js API route implementation
- User wants to see organizations + 2FA in practice
- User asks for PostgreSQL setup with Drizzle
Framework-Specific Setup
Load when:
references/frameworks/nextjs.md- User building with Next.js (App Router or Pages Router)
- User needs middleware, Server Components, or API routes
Load when:
references/frameworks/nuxt.md- User building with Nuxt 3
- User needs H3 handlers, composables, or server routes
Load when:
references/frameworks/remix.md- User building with Remix
- User needs loader/action patterns or session handling
Load when:
references/frameworks/sveltekit.md- User building with SvelteKit
- User needs hooks, load functions, or stores
Load when:
references/frameworks/api-frameworks.md- User building with Express, Fastify, NestJS, or Hono (non-Cloudflare)
- User needs middleware or route configuration
Load when:
references/frameworks/expo-mobile.md- User building React Native or Expo app
- User needs SecureStore, deep linking, or mobile auth
Database Adapters
Load when:
references/databases/postgresql.md- User using PostgreSQL with Drizzle or Prisma
- User needs Neon, Supabase, or connection pooling setup
Load when:
references/databases/mongodb.md- User using MongoDB
- User needs Atlas setup or indexes
Load when:
references/databases/mysql.md- User using MySQL or PlanetScale
- User needs Vitess compatibility guidance
Plugin Guides
Load when:
references/plugins/authentication.md- User needs 2FA, passkeys, magic links, email OTP, or anonymous users
- User asks about enhanced authentication methods
Load when:
references/plugins/enterprise.md- User needs organizations, SSO/SAML, SCIM, or admin dashboard
- User building multi-tenant or enterprise application
Load when:
references/plugins/api-tokens.md- User needs API keys, bearer tokens, JWT, or OIDC provider
- User building API authentication for third parties
Load when:
references/plugins/payments.md- User needs Stripe or Polar integration
- User building subscription or payment features
Configuration Reference
Quick Config (ESM-only in v1.4.0+):
typescript
export const auth = betterAuth({
baseURL: env.BETTER_AUTH_URL,
secret: env.BETTER_AUTH_SECRET,
database: drizzleAdapter(db, { provider: "sqlite" }),
});Load for:
references/configuration-guide.md- Production configuration with email/password and social providers
- wrangler.toml setup and environment variables
- Session configuration, CORS setup, and ESM requirements
- Rate limiting, API keys (v1.4.0+), and troubleshooting
Using Bundled Resources
References (references/)
- setup-guide.md - Complete 8-step setup (D1 → Drizzle → Deploy)
- error-catalog.md - All 12 errors with solutions and prevention checklist
- advanced-features.md - 2FA, organizations, rate limiting, passkeys, magic links, migrations
- configuration-guide.md - Production config, environment variables, CORS, rate limiting
- framework-comparison.md - better-auth vs Clerk vs Auth.js, migration paths, TCO
- migration-guide-1.4.0.md - Upgrading from v1.3.x to v1.4.0+ (ESM, API changes)
- cloudflare-worker-drizzle.ts - Complete Worker with Drizzle auth
- cloudflare-worker-kysely.ts - Complete Worker with Kysely auth
- database-schema.ts - Complete better-auth Drizzle schema
- react-client-hooks.tsx - React components with auth hooks
- v1.4-features.md - Background tasks, new OAuth providers, SAML/SSO, CLI
Framework References (references/frameworks/)
- nextjs.md - Next.js App/Pages Router integration
- nuxt.md - Nuxt 3 with H3 and composables
- remix.md - Remix loaders, actions, sessions
- sveltekit.md - SvelteKit hooks and stores
- api-frameworks.md - Express, Fastify, NestJS, Hono
- expo-mobile.md - React Native and Expo
Database References (references/databases/)
- postgresql.md - PostgreSQL with Drizzle/Prisma, Neon/Supabase
- mongodb.md - MongoDB adapter and Atlas
- mysql.md - MySQL and PlanetScale
Plugin References (references/plugins/)
- authentication.md - 2FA, passkeys, magic links, email OTP, anonymous
- enterprise.md - Organizations, SSO, SCIM, admin
- api-tokens.md - API keys, bearer tokens, JWT, OIDC
- payments.md - Stripe, Polar integrations
Next.js Examples (references/nextjs/)
- README.md - Next.js + PostgreSQL setup guide (not D1)
- postgres-example.ts - Complete API route with organizations, 2FA, email verification
Client Integration
Create auth client ():
src/lib/auth-client.tstypescript
import { createAuthClient } from "better-auth/client";
export const authClient = createAuthClient({
baseURL: import.meta.env.VITE_API_URL || "http://localhost:8787",
});Use in React:
typescript
import { authClient } from "@/lib/auth-client";
export function UserProfile() {
const { data: session, isPending } = authClient.useSession();
if (isPending) return <div>Loading...</div>;
if (!session) return <div>Not authenticated</div>;
return (
<div>
<p>Welcome, {session.user.email}</p>
<button onClick={() => authClient.signOut()}>Sign Out</button>
</div>
);
}Dependencies
Required:
- - Core authentication framework (ESM-only)
better-auth@^1.4.9
Choose ONE adapter:
- +
drizzle-orm@^0.44.7(recommended)drizzle-kit@^0.31.7 - +
kysely@^0.28.8(alternative)@noxharmonium/kysely-d1@^0.4.0
Optional:
- - TypeScript types for Workers
@cloudflare/workers-types - - Web framework for routing
hono@^4.0.0 - - Passkey plugin (v1.4.0+, separate package)
@better-auth/passkey - - API key auth (v1.4.0+)
@better-auth/api-key
Beyond Cloudflare D1
This skill focuses on Cloudflare Workers + D1. better-auth also supports:
Frameworks (18 total): Next.js, Nuxt, Remix, SvelteKit, Astro, Express, NestJS, Fastify, Elysia, Expo, and more.
Databases (9 adapters): PostgreSQL, MongoDB, MySQL, Prisma, MS SQL, and others.
Additional Plugins: Anonymous auth, Email OTP, JWT, Multi-Session, OIDC Provider, payment integrations (Stripe, Polar).
For non-Cloudflare setups, load the appropriate framework or database reference file, or consult the official docs: https://better-auth.com/docs
Official Documentation
- better-auth Docs: https://better-auth.com
- GitHub: https://github.com/better-auth/better-auth (22.4k ⭐)
- Examples: https://github.com/better-auth/better-auth/tree/main/examples
- Drizzle Docs: https://orm.drizzle.team/docs/get-started-sqlite
- Kysely Docs: https://kysely.dev/
- Discord: https://discord.gg/better-auth
Framework Comparison
Load for:
references/framework-comparison.md- Complete feature comparison: better-auth vs Clerk vs Auth.js
- v1.4.0+ new features (database joins, stateless sessions, API keys)
- Migration paths, cost analysis, and performance benchmarks
- Recommendations by use case and 5-year TCO
Production Examples
Verified working repositories (all use Drizzle or Kysely):
- zwily/example-react-router-cloudflare-d1-drizzle-better-auth - Drizzle
- matthewlynch/better-auth-react-router-cloudflare-d1 - Kysely
- foxlau/react-router-v7-better-auth - Drizzle
- zpg6/better-auth-cloudflare - Drizzle (includes CLI)
Note: Check each repo's better-auth version. Repos on v1.3.x need v1.4.0+ migration (see ). None use a direct - all require Drizzle/Kysely.
references/migration-guide-1.4.0.mdd1AdapterComplete Setup Checklist
- Verified ESM support (in package.json) - v1.4.0+ required
"type": "module" - Installed better-auth@1.4.9+ + Drizzle OR Kysely
- Created D1 database with wrangler
- Defined database schema with required tables (user, session, account, verification)
- Generated and applied migrations to D1
- Set BETTER_AUTH_SECRET environment variable
- Configured baseURL in auth config
- Enabled authentication methods (emailAndPassword, socialProviders)
- Configured CORS with credentials: true
- Set OAuth callback URLs in provider settings
- Tested auth routes (/api/auth/*)
- Tested sign-in, sign-up, session verification
- Using requestPasswordReset (not forgetPassword) - v1.4.0+ API
- Deployed to Cloudflare Workers
Questions? Issues?
- Check for all 15 errors and solutions
references/error-catalog.md - Review for complete 8-step setup
references/setup-guide.md - See for 2FA, organizations, and more
references/advanced-features.md - Check official docs: https://better-auth.com
- Ensure you're using Drizzle or Kysely (not non-existent d1Adapter)