better-auth-best-practices
Original:🇺🇸 English
Translated
Skill for integrating Better Auth - the comprehensive TypeScript authentication framework.
45.3kinstalls
Sourcebetter-auth/skills
Added on
NPX Install
npx skill4agent add better-auth/skills better-auth-best-practicesTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →Better Auth Integration Guide
Always consult better-auth.com/docs for code examples and latest API.
Better Auth is a TypeScript-first, framework-agnostic auth framework supporting email/password, OAuth, magic links, passkeys, and more via plugins.
Quick Reference
Environment Variables
- - Encryption secret (min 32 chars). Generate:
BETTER_AUTH_SECRETopenssl rand -base64 32 - - Base URL (e.g.,
BETTER_AUTH_URL)https://example.com
Only define / in config if env vars are NOT set.
baseURLsecretFile Location
CLI looks for in: , , , or under . Use for custom path.
auth.ts././lib./utils./src--configCLI Commands
- - Apply schema (built-in adapter)
npx @better-auth/cli@latest migrate - - Generate schema for Prisma/Drizzle
npx @better-auth/cli@latest generate - - Add MCP to AI tools
npx @better-auth/cli mcp --cursor
Re-run after adding/changing plugins.
Core Config Options
| Option | Notes |
|---|---|
| Optional display name |
| Only if |
| Default |
| Only if |
| Required for most features. See adapters docs. |
| Redis/KV for sessions & rate limits |
| |
| |
| Array of plugins |
| CSRF whitelist |
Database
Direct connections: Pass , pool, , or instance.
pg.Poolmysql2better-sqlite3bun:sqliteORM adapters: Import from , , .
better-auth/adapters/drizzlebetter-auth/adapters/prismabetter-auth/adapters/mongodbCritical: Better Auth uses adapter model names, NOT underlying table names. If Prisma model is mapping to table , use (Prisma reference), not .
UserusersmodelName: "user""users"Session Management
Storage priority:
- If defined → sessions go there (not DB)
secondaryStorage - Set to also persist to DB
session.storeSessionInDatabase: true - No database + → fully stateless mode
cookieCache
Cookie cache strategies:
- (default) - Base64url + HMAC. Smallest.
compact - - Standard JWT. Readable but signed.
jwt - - Encrypted. Maximum security.
jwe
Key options: (default 7 days), (refresh interval), , (change to invalidate all sessions).
session.expiresInsession.updateAgesession.cookieCache.maxAgesession.cookieCache.versionUser & Account Config
User: , (column mapping), , (disabled by default), (disabled by default).
user.modelNameuser.fieldsuser.additionalFieldsuser.changeEmail.enableduser.deleteUser.enabledAccount: , , (for stateless OAuth).
account.modelNameaccount.accountLinking.enabledaccount.storeAccountCookieRequired for registration: and fields.
emailnameEmail Flows
- - Must be defined for verification to work
emailVerification.sendVerificationEmail - /
emailVerification.sendOnSignUp- Auto-send triggerssendOnSignIn - - Password reset email handler
emailAndPassword.sendResetPassword
Security
In :
advanced- - Force HTTPS cookies
useSecureCookies - - ⚠️ Security risk
disableCSRFCheck - - ⚠️ Security risk
disableOriginCheck - - Share cookies across subdomains
crossSubDomainCookies.enabled - - Custom IP headers for proxies
ipAddress.ipAddressHeaders - - Custom ID generation or
database.generateId/"serial"/"uuid"false
Rate limiting: , , , ("memory" | "database" | "secondary-storage").
rateLimit.enabledrateLimit.windowrateLimit.maxrateLimit.storageHooks
Endpoint hooks: / - Array of . Use . Access , (after), .
hooks.beforehooks.after{ matcher, handler }createAuthMiddlewarectx.pathctx.context.returnedctx.context.sessionDatabase hooks: , same for , . Useful for adding default values or post-creation actions.
databaseHooks.user.create.before/aftersessionaccountHook context (): , , , /, , , , , .
ctx.contextsessionsecretauthCookiespassword.hash()verify()adapterinternalAdaptergenerateId()tablesbaseURLPlugins
Import from dedicated paths for tree-shaking:
import { twoFactor } from "better-auth/plugins/two-factor"NOT .
from "better-auth/plugins"Popular plugins: , , , , , , , , , , , , , , , , .
twoFactororganizationpasskeymagicLinkemailOtpusernamephoneNumberadminapiKeybearerjwtmultiSessionssooauthProvideroidcProvideropenAPIgenericOAuthClient plugins go in .
createAuthClient({ plugins: [...] })Client
Import from: (vanilla), , , , .
better-auth/clientbetter-auth/reactbetter-auth/vuebetter-auth/sveltebetter-auth/solidKey methods: , , , , , , , .
signUp.email()signIn.email()signIn.social()signOut()useSession()getSession()revokeSession()revokeSessions()Type Safety
Infer types: , .
typeof auth.$Infer.Sessiontypeof auth.$Infer.Session.userFor separate client/server projects: .
createAuthClient<typeof auth>()Common Gotchas
- Model vs table name - Config uses ORM model name, not DB table name
- Plugin schema - Re-run CLI after adding plugins
- Secondary storage - Sessions go there by default, not DB
- Cookie cache - Custom session fields NOT cached, always re-fetched
- Stateless mode - No DB = session in cookie only, logout on cache expiry
- Change email flow - Sends to current email first, then new email