better-auth

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.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "better-auth" with this command: npx skills add better-auth/skills

better-auth

Package: better-auth@1.4.15 (ESM-only since v1.4.0) Docs: https://better-auth.com/docs | GitHub: https://github.com/better-auth/better-auth

Environment Setup

VariablePurpose
BETTER_AUTH_SECRETEncryption secret (min 32 chars). Generate: openssl rand -base64 32
BETTER_AUTH_URLBase URL (e.g., https://example.com)

Only define baseURL/secret in config if env vars are NOT set. CLI looks for auth.ts in: ./, ./lib, ./utils, or ./src.

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 unless using stateless mode (v1.4+)
secondaryStorageRedis/KV for sessions and rate limits
emailAndPassword{ enabled: true } to activate
socialProviders{ google: { clientId, clientSecret }, ... }
pluginsArray of plugins
trustedOriginsCSRF whitelist

Plugin Reference

PluginDescription
twoFactorTOTP, email OTP, backup codes
organizationMulti-tenant orgs, teams, invitations, RBAC
adminUser management, impersonation, banning
passkeyWebAuthn passwordless login
magicLinkEmail-based passwordless login
jwtJWT tokens with key rotation, JWKS
oauthProviderBuild your own OAuth 2.1 provider (separate @better-auth/oauth-provider package)
ssoEnterprise SSO with OIDC, OAuth2, SAML 2.0 (separate @better-auth/sso package)
scimEnterprise user provisioning (separate @better-auth/scim package)
stripePayment and subscription management
bearerAPI token auth for mobile/CLI
apiKeyToken-based auth with rate limits
oneTapGoogle One Tap frictionless sign-in
anonymousGuest user access without PII
genericOAuthCustom OAuth providers with PKCE
emailOTPEmail-based one-time password auth
phoneNumberPhone/SMS-based OTP sign-in
usernameUsername-based sign-in (alternative to email)
multiSessionMultiple accounts in same browser
openAPIInteractive API docs at /api/auth/reference

Session Strategies

StrategyFormatUse Case
Compact (default)Base64url + HMAC-SHA256Smallest, fastest
JWTStandard JWTInteroperable
JWEA256CBC-HS512 encryptedMost secure

Getting Started

For new projects or first-time Better Auth setup, use the official interactive setup skill:

npx skills add better-auth/skills -s create-auth-skill

This walks through framework detection, database selection, auth method choices, plugin setup, and generates the initial configuration.

Anti-Patterns

Anti-PatternCorrect Approach
Using d1AdapterUse Drizzle or Kysely adapter with provider: "sqlite"
Using table name in configUse ORM model name, not DB table name
Forgetting CLI after plugin changesRe-run npx @better-auth/cli@latest generate
tanstackStartCookies() not last pluginMust be the last plugin in array (TanStack Start)
Checking session for login stateCheck session?.user — session object is always truthy
Missing nodejs_compat flagRequired in wrangler.toml for Cloudflare Workers
Kysely CamelCasePlugin with authUse separate Kysely instance without the plugin
Using old reactStartCookies importRenamed to tanstackStartCookies from better-auth/tanstack-start in v1.4.14

Common Mistakes

MistakeCorrect Pattern
Setting baseURL and secret in config when env vars are already setOnly define these in config if BETTER_AUTH_URL and BETTER_AUTH_SECRET env vars are NOT set
Using CommonJS require syntax with better-auth v1.4+better-auth is ESM-only since v1.4.0; use import syntax exclusively
Not re-running CLI generate after adding or changing pluginsAlways run npx @better-auth/cli@latest generate after plugin changes to update DB schema
Checking session object truthy state for login detectionCheck session?.user instead; the session object itself is always truthy
Using d1Adapter directly for Cloudflare D1Use Drizzle or Kysely adapter with provider: "sqlite" for D1 compatibility

Breaking Changes

VersionChange
v1.4.14reactStartCookies renamed to tanstackStartCookies (import from better-auth/tanstack-start)
v1.4.6allowImpersonatingAdmins defaults to false
v1.4.0ESM-only (no CommonJS); SSO, SCIM, OAuth Provider moved to separate packages
v1.3.0Multi-team table structure: new teamMembers table needed

Delegation

When working on auth, delegate to:

  • application-security — Security architecture and threat modeling
  • database — Drizzle ORM schema and migrations
  • tanstack-start — TanStack Start integration patterns

Resources

References

  • Database Adapters — Drizzle, Kysely, Prisma adapters, Cloudflare Workers factory pattern
  • Session Management — Cookie cache, stateless sessions, storage priority, freshAge constraints
  • Plugins and Social Auth — Plugin setup, OAuth 2.1 provider, admin RBAC, social provider scopes
  • Email and Password — Verification, password reset, timing attack prevention, hashing (scrypt, argon2), token security
  • Two-Factor Authentication — TOTP, email/SMS OTP, backup codes, trusted devices, 2FA session flow
  • Organizations — Multi-tenant orgs, teams, invitations, RBAC, dynamic access control, lifecycle hooks
  • Configuration — User/account config, rate limiting, hooks, CSRF, trusted origins, cookie/OAuth security, production checklist
  • Framework Integration — TanStack Start setup, Expo/React Native, client imports, type safety
  • Migration Guides — Migrate from NextAuth/Auth.js, Clerk, or Supabase Auth with schema mappings and session strategies
  • Troubleshooting — D1 consistency, CORS, OAuth redirect, admin 403, nanostore refresh, known bugs

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Security

application-security

No summary provided by upstream source.

Repository SourceNeeds Review
Security

database-security

No summary provided by upstream source.

Repository SourceNeeds Review
Security

quality-auditor

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

playwright

No summary provided by upstream source.

Repository SourceNeeds Review