---
id: better-auth-best-practices
name: "better-auth-best-practices"
url: https://skills.yangsir.net/skill/better-auth-best-practices
author: better-auth
domain: ai-agent-external-interaction
tags: ["api", "database", "better", "auth", "react"]
install_count: 125600
rating: 4.80 (1268 reviews)
github: https://github.com/better-auth/skills
---

# better-auth-best-practices

> 提供Better Auth集成指南和最佳实践，指导用户安装、配置环境变量，并查阅官方文档获取最新API和代码示例。

**Stats**: 125,600 installs · 4.8/5 (1268 reviews)

## Before / After 对比

### Better Auth集成与安全性对比

## Readme

# better-auth-best-practices

# Better Auth Integration Guide

**Always consult [better-auth.com/docs](https://better-auth.com/docs) for code examples and latest API.**

## Setup Workflow

- Install: `npm install better-auth`

- Set env vars: `BETTER_AUTH_SECRET` and `BETTER_AUTH_URL`

- Create `auth.ts` with database + config

- Create route handler for your framework

- Run `npx @better-auth/cli@latest migrate`

- Verify: call `GET /api/auth/ok` — should return `{ status: "ok" }`

## Quick Reference

### Environment Variables

- `BETTER_AUTH_SECRET` - Encryption secret (min 32 chars). Generate: `openssl rand -base64 32`

- `BETTER_AUTH_URL` - Base URL (e.g., `https://example.com`)

Only define `baseURL`/`secret` in config if env vars are NOT set.

### File Location

CLI looks for `auth.ts` in: `./`, `./lib`, `./utils`, or under `./src`. Use `--config` for custom path.

### CLI Commands

- `npx @better-auth/cli@latest migrate` - Apply schema (built-in adapter)

- `npx @better-auth/cli@latest generate` - Generate schema for Prisma/Drizzle

- `npx @better-auth/cli mcp --cursor` - Add MCP to AI tools

**Re-run after adding/changing plugins.**

## Core Config Options

Option
Notes

`appName`
Optional display name

`baseURL`
Only if `BETTER_AUTH_URL` not set

`basePath`
Default `/api/auth`. Set `/` for root.

`secret`
Only if `BETTER_AUTH_SECRET` not set

`database`
Required for most features. See adapters docs.

`secondaryStorage`
Redis/KV for sessions & rate limits

`emailAndPassword`
`{ enabled: true }` to activate

`socialProviders`
`{ google: { clientId, clientSecret }, ... }`

`plugins`
Array of plugins

`trustedOrigins`
CSRF whitelist

## Database

**Direct connections:** Pass `pg.Pool`, `mysql2` pool, `better-sqlite3`, or `bun:sqlite` instance.

**ORM adapters:** Import from `better-auth/adapters/drizzle`, `better-auth/adapters/prisma`, `better-auth/adapters/mongodb`.

**Critical:** Better Auth uses adapter model names, NOT underlying table names. If Prisma model is `User` mapping to table `users`, use `modelName: "user"` (Prisma reference), not `"users"`.

## Session Management

**Storage priority:**

- If `secondaryStorage` defined → sessions go there (not DB)

- Set `session.storeSessionInDatabase: true` to also persist to DB

- No database + `cookieCache` → fully stateless mode

**Cookie cache strategies:**

- `compact` (default) - Base64url + HMAC. Smallest.

- `jwt` - Standard JWT. Readable but signed.

- `jwe` - Encrypted. Maximum security.

**Key options:** `session.expiresIn` (default 7 days), `session.updateAge` (refresh interval), `session.cookieCache.maxAge`, `session.cookieCache.version` (change to invalidate all sessions).

## User & Account Config

**User:** `user.modelName`, `user.fields` (column mapping), `user.additionalFields`, `user.changeEmail.enabled` (disabled by default), `user.deleteUser.enabled` (disabled by default).

**Account:** `account.modelName`, `account.accountLinking.enabled`, `account.storeAccountCookie` (for stateless OAuth).

**Required for registration:** `email` and `name` fields.

## Email Flows

- `emailVerification.sendVerificationEmail` - Must be defined for verification to work

- `emailVerification.sendOnSignUp` / `sendOnSignIn` - Auto-send triggers

- `emailAndPassword.sendResetPassword` - Password reset email handler

## Security

**In `advanced`:**

- `useSecureCookies` - Force HTTPS cookies

- `disableCSRFCheck` - ⚠️ Security risk

- `disableOriginCheck` - ⚠️ Security risk

- `crossSubDomainCookies.enabled` - Share cookies across subdomains

- `ipAddress.ipAddressHeaders` - Custom IP headers for proxies

- `database.generateId` - Custom ID generation or `"serial"`/`"uuid"`/`false`

**Rate limiting:** `rateLimit.enabled`, `rateLimit.window`, `rateLimit.max`, `rateLimit.storage` ("memory" | "database" | "secondary-storage").

## Hooks

**Endpoint hooks:** `hooks.before` / `hooks.after` - Array of `{ matcher, handler }`. Use `createAuthMiddleware`. Access `ctx.path`, `ctx.context.returned` (after), `ctx.context.session`.

**Database hooks:** `databaseHooks.user.create.before/after`, same for `session`, `account`. Useful for adding default values or post-creation actions.

**Hook context (`ctx.context`):** `session`, `secret`, `authCookies`, `password.hash()`/`verify()`, `adapter`, `internalAdapter`, `generateId()`, `tables`, `baseURL`.

## Plugins

**Import from dedicated paths for tree-shaking:**

```
import { twoFactor } from "better-auth/plugins/two-factor"

```

NOT `from "better-auth/plugins"`.

**Popular plugins:** `twoFactor`, `organization`, `passkey`, `magicLink`, `emailOtp`, `username`, `phoneNumber`, `admin`, `apiKey`, `bearer`, `jwt`, `multiSession`, `sso`, `oauthProvider`, `oidcProvider`, `openAPI`, `genericOAuth`.

Client plugins go in `createAuthClient({ plugins: [...] })`.

## Client

Import from: `better-auth/client` (vanilla), `better-auth/react`, `better-auth/vue`, `better-auth/svelte`, `better-auth/solid`.

Key methods: `signUp.email()`, `signIn.email()`, `signIn.social()`, `signOut()`, `useSession()`, `getSession()`, `revokeSession()`, `revokeSessions()`.

## Type Safety

Infer types: `typeof auth.$Infer.Session`, `typeof auth.$Infer.Session.user`.

For 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

## Resources

- [Docs](https://better-auth.com/docs)

- [Options Reference](https://better-auth.com/docs/reference/options)

- [LLMs.txt](https://better-auth.com/llms.txt)

- [GitHub](https://github.com/better-auth/better-auth)

- [Init Options Source](https://github.com/better-auth/better-auth/blob/main/packages/core/src/types/init-options.ts)

Weekly Installs24.3KRepository[better-auth/skills](https://github.com/better-auth/skills)GitHub Stars158First SeenJan 19, 2026Security Audits[Gen Agent Trust HubPass](/better-auth/skills/better-auth-best-practices/security/agent-trust-hub)[SocketPass](/better-auth/skills/better-auth-best-practices/security/socket)[SnykPass](/better-auth/skills/better-auth-best-practices/security/snyk)Installed onopencode16.9Kcodex16.1Kgithub-copilot15.8Kgemini-cli15.7Kcursor14.8Kclaude-code14.4K

---
*Source: https://skills.yangsir.net/skill/better-auth-best-practices*
*Markdown mirror: https://skills.yangsir.net/api/skill/better-auth-best-practices/markdown*