1---2name: better-auth3description: Self-hosted TypeScript auth framework with social auth, 2FA, passkeys, organizations, RBAC, and 15+ plugins. Supports Drizzle/Prisma/Kysely adapters. Self-hosted alternative to Clerk/Auth.js. Use when: configuring auth, adding plugins, social OAuth, multi-tenant SaaS, organizations with teams and RBAC, two-factor authentication (TOTP/OTP/backup codes), email verification, password reset flows, session management, rate limiting, CSRF and cookie security, Expo/mobile, D1 adapter errors, TanStack Start integration, additionalFields bugs, admin plugin, migrating from NextAuth, migrating from Clerk, migrating from Supabase Auth, or troubleshooting auth issues.4license: MIT5---6
7# better-auth
8
9**Package**: better-auth@1.4.15 (ESM-only since v1.4.0)
10**Docs**: <https://better-auth.com/docs> | **GitHub**: <https://github.com/better-auth/better-auth>
11
12## Environment Setup
13
14| Variable | Purpose |
15| -------------------- | --------------------------------------------------------------------- |
16| `BETTER_AUTH_SECRET` | Encryption secret (min 32 chars). Generate: `openssl rand -base64 32` |
17| `BETTER_AUTH_URL` | Base URL (e.g., `https://example.com`) |
18
19Only define `baseURL`/`secret` in config if env vars are NOT set. CLI looks for `auth.ts` in: `./`, `./lib`, `./utils`, or `./src`.
20
21## Core Config Options
22
23| Option | Notes |
24| ------------------ | --------------------------------------------- |
25| `appName` | Optional display name |
26| `baseURL` | Only if `BETTER_AUTH_URL` not set |
27| `basePath` | Default `/api/auth`. Set `/` for root. |
28| `secret` | Only if `BETTER_AUTH_SECRET` not set |
29| `database` | Required unless using stateless mode (v1.4+) |
30| `secondaryStorage` | Redis/KV for sessions and rate limits |
31| `emailAndPassword` | `{ enabled: true }` to activate |
32| `socialProviders` | `{ google: { clientId, clientSecret }, ... }` |
33| `plugins` | Array of plugins |
34| `trustedOrigins` | CSRF whitelist |
35
36## Plugin Reference
37
38| Plugin | Description |
39| ------------- | ---------------------------------------------------------------------------------- |
40| twoFactor | TOTP, email OTP, backup codes |
41| organization | Multi-tenant orgs, teams, invitations, RBAC |
42| admin | User management, impersonation, banning |
43| passkey | WebAuthn passwordless login |
44| magicLink | Email-based passwordless login |
45| jwt | JWT tokens with key rotation, JWKS |
46| oauthProvider | Build your own OAuth 2.1 provider (separate `@better-auth/oauth-provider` package) |
47| sso | Enterprise SSO with OIDC, OAuth2, SAML 2.0 (separate `@better-auth/sso` package) |
48| scim | Enterprise user provisioning (separate `@better-auth/scim` package) |
49| stripe | Payment and subscription management |
50| bearer | API token auth for mobile/CLI |
51| apiKey | Token-based auth with rate limits |
52| oneTap | Google One Tap frictionless sign-in |
53| anonymous | Guest user access without PII |
54| genericOAuth | Custom OAuth providers with PKCE |
55| emailOTP | Email-based one-time password auth |
56| phoneNumber | Phone/SMS-based OTP sign-in |
57| username | Username-based sign-in (alternative to email) |
58| multiSession | Multiple accounts in same browser |
59| openAPI | Interactive API docs at `/api/auth/reference` |
60
61## Session Strategies
62
63| Strategy | Format | Use Case |
64| ----------------- | ----------------------- | ----------------- |
65| Compact (default) | Base64url + HMAC-SHA256 | Smallest, fastest |
66| JWT | Standard JWT | Interoperable |
67| JWE | A256CBC-HS512 encrypted | Most secure |
68
69## Getting Started
70
71For new projects or first-time Better Auth setup, use the official interactive setup skill:
72
73```bash
74npx skills add better-auth/skills -s create-auth-skill
75```
76
77This walks through framework detection, database selection, auth method choices, plugin setup, and generates the initial configuration.
78
79## Anti-Patterns
80
81| Anti-Pattern | Correct Approach |
82| ---------------------------------------- | ------------------------------------------------------------------------------ |
83| Using `d1Adapter` | Use Drizzle or Kysely adapter with `provider: "sqlite"` |
84| Using table name in config | Use ORM model name, not DB table name |
85| Forgetting CLI after plugin changes | Re-run `npx @better-auth/cli@latest generate` |
86| `tanstackStartCookies()` not last plugin | Must be the last plugin in array (TanStack Start) |
87| Checking `session` for login state | Check `session?.user` — session object is always truthy |
88| Missing `nodejs_compat` flag | Required in `wrangler.toml` for Cloudflare Workers |
89| Kysely CamelCasePlugin with auth | Use separate Kysely instance without the plugin |
90| Using old `reactStartCookies` import | Renamed to `tanstackStartCookies` from `better-auth/tanstack-start` in v1.4.14 |
91
92## Common Mistakes
93
94| Mistake | Correct Pattern |
95| ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
96| Setting `baseURL` and `secret` in config when env vars are already set | Only define these in config if `BETTER_AUTH_URL` and `BETTER_AUTH_SECRET` env vars are NOT set |
97| Using CommonJS require syntax with better-auth v1.4+ | better-auth is ESM-only since v1.4.0; use `import` syntax exclusively |
98| Not re-running CLI generate after adding or changing plugins | Always run `npx @better-auth/cli@latest generate` after plugin changes to update DB schema |
99| Checking `session` object truthy state for login detection | Check `session?.user` instead; the session object itself is always truthy |
100| Using `d1Adapter` directly for Cloudflare D1 | Use Drizzle or Kysely adapter with `provider: "sqlite"` for D1 compatibility |
101
102## Breaking Changes
103
104| Version | Change |
105| ------- | ------------------------------------------------------------------------------------------------ |
106| v1.4.14 | `reactStartCookies` renamed to `tanstackStartCookies` (import from `better-auth/tanstack-start`) |
107| v1.4.6 | `allowImpersonatingAdmins` defaults to `false` |
108| v1.4.0 | ESM-only (no CommonJS); SSO, SCIM, OAuth Provider moved to separate packages |
109| v1.3.0 | Multi-team table structure: new `teamMembers` table needed |
110
111## Delegation
112
113When working on auth, delegate to:
114
115- `application-security` — Security architecture and threat modeling
116- `database` — Drizzle ORM schema and migrations
117- `tanstack-start` — TanStack Start integration patterns
118
119## Resources
120
121- **Docs**: <https://better-auth.com/docs>
122- **Options Reference**: <https://better-auth.com/docs/reference/options>
123- **LLMs.txt**: <https://better-auth.com/llms.txt>
124- **Changelog**: <https://www.better-auth.com/changelogs>
125- **TanStack Start**: <https://www.better-auth.com/docs/integrations/tanstack>
126- **Expo**: <https://www.better-auth.com/docs/integrations/expo>
127
128## References
129
130- [Database Adapters](references/database-adapters.md) — Drizzle, Kysely, Prisma adapters, Cloudflare Workers factory pattern
131- [Session Management](references/sessions.md) — Cookie cache, stateless sessions, storage priority, freshAge constraints
132- [Plugins and Social Auth](references/plugins.md) — Plugin setup, OAuth 2.1 provider, admin RBAC, social provider scopes
133- [Email and Password](references/email-password.md) — Verification, password reset, timing attack prevention, hashing (scrypt, argon2), token security
134- [Two-Factor Authentication](references/two-factor.md) — TOTP, email/SMS OTP, backup codes, trusted devices, 2FA session flow
135- [Organizations](references/organizations.md) — Multi-tenant orgs, teams, invitations, RBAC, dynamic access control, lifecycle hooks
136- [Configuration](references/configuration.md) — User/account config, rate limiting, hooks, CSRF, trusted origins, cookie/OAuth security, production checklist
137- [Framework Integration](references/frameworks.md) — TanStack Start setup, Expo/React Native, client imports, type safety
138- [Migration Guides](references/migration-guides.md) — Migrate from NextAuth/Auth.js, Clerk, or Supabase Auth with schema mappings and session strategies
139- [Troubleshooting](references/troubleshooting.md) — D1 consistency, CORS, OAuth redirect, admin 403, nanostore refresh, known bugs