|
| 1 | +--- |
| 2 | +name: use-better-auth |
| 3 | +description: 'Answer questions about better-auth and help build authentication features. Use when developers: (1) ask about better-auth APIs like `betterAuth`, `auth.api.*`, `authClient.*`; (2) wire up sign-in / sign-up / sessions; (3) integrate framework adapters (Next.js, Nuxt, SvelteKit, Hono, Astro, Bun, Express); (4) add plugins (organization, two-factor, magic-link, passkey, oauth-proxy); (5) configure DB adapters (Prisma, Drizzle, Kysely, Mongoose). Triggers on: "better-auth", "betterAuth", "authClient", "sign in flow", "auth session", "social login", "magic link", "passkey", "organization plugin".' |
| 4 | +--- |
| 5 | + |
| 6 | +## Prerequisites |
| 7 | + |
| 8 | +Before writing code, verify `better-auth` is in the project's lockfile (`bun.lock`, `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, or `package.json`). If not installed, install with the project's package manager: |
| 9 | + |
| 10 | +```bash |
| 11 | +bun add better-auth # or: pnpm add better-auth / npm i better-auth / yarn add better-auth |
| 12 | +``` |
| 13 | + |
| 14 | +Verify the `ask` CLI is available (`which ask`). It is the primary tool for reading the exact version installed in this project. If `ask` is not installed, fall back to `node_modules/better-auth/` and the official site `https://better-auth.com/docs`. |
| 15 | + |
| 16 | +## Critical: Do Not Trust Internal Knowledge |
| 17 | + |
| 18 | +Everything known about better-auth from training data is suspect. The library's public API, plugin surface, framework adapters, and DB-adapter shapes shift between minor versions. Ship code against the current source, not memory. |
| 19 | + |
| 20 | +When working with better-auth: |
| 21 | + |
| 22 | +1. Resolve the installed version against the local checkout with `ask` |
| 23 | +2. Verify plugin names, adapter imports, and option shapes before generating code |
| 24 | +3. Run typecheck after changes to surface silent breakage early |
| 25 | +4. Never invent plugin names, adapter import paths, or function signatures — enumerate them first |
| 26 | +5. Surface deprecations or conflicts to the user rather than silently picking one pattern |
| 27 | + |
| 28 | +If documentation cannot be found to support an answer, state that explicitly. |
| 29 | + |
| 30 | +## Finding Documentation |
| 31 | + |
| 32 | +Resolve the source checkout once; reuse the path across reads: |
| 33 | + |
| 34 | +```bash |
| 35 | +SRC=$(ask src better-auth) # checkout root |
| 36 | +DOCS=$(ask docs better-auth | head -n1) # candidate docs dir |
| 37 | +``` |
| 38 | + |
| 39 | +### Read the README and docs content |
| 40 | + |
| 41 | +```bash |
| 42 | +cat "$DOCS/README.md" |
| 43 | +ls "$SRC/docs/content/docs" |
| 44 | +cat "$SRC/docs/content/docs/installation.mdx" |
| 45 | +cat "$SRC/docs/content/docs/basic-usage.mdx" |
| 46 | +``` |
| 47 | + |
| 48 | +### Enumerate plugins (authoritative list) |
| 49 | + |
| 50 | +```bash |
| 51 | +ls "$SRC/packages/better-auth/src/plugins" |
| 52 | +``` |
| 53 | + |
| 54 | +### Enumerate framework integrations |
| 55 | + |
| 56 | +```bash |
| 57 | +ls "$SRC/packages/better-auth/src/integrations" |
| 58 | +ls "$SRC/docs/content/docs/integrations" |
| 59 | +``` |
| 60 | + |
| 61 | +Note: the source-level `integrations/` directory only contains helper functions (Next.js cookies, SvelteKit handler, Node handler, etc.). Most frameworks (Hono, Astro, Express, Nuxt) mount the generic `auth.handler` directly — check `references/adapters.md` for the per-framework pattern. |
| 62 | + |
| 63 | +### Enumerate DB adapters |
| 64 | + |
| 65 | +```bash |
| 66 | +ls "$SRC/packages/better-auth/src/adapters" |
| 67 | +ls "$SRC/packages" | grep adapter |
| 68 | +``` |
| 69 | + |
| 70 | +### Enumerate social providers |
| 71 | + |
| 72 | +```bash |
| 73 | +ls "$SRC/packages/core/src/social-providers" |
| 74 | +``` |
| 75 | + |
| 76 | +### Grep for exported API |
| 77 | + |
| 78 | +```bash |
| 79 | +rg "^export " "$SRC/packages/better-auth/src/index.ts" |
| 80 | +rg "^export " "$SRC/packages/better-auth/src/client/index.ts" |
| 81 | +rg "^export (const|function) createAuthClient" "$SRC/packages/better-auth/src/client/" |
| 82 | +``` |
| 83 | + |
| 84 | +### Fallback when `ask` is unavailable |
| 85 | + |
| 86 | +```bash |
| 87 | +SRC=./node_modules/better-auth |
| 88 | +ls $SRC/dist |
| 89 | +rg "betterAuth\\(" $SRC/dist |
| 90 | +``` |
| 91 | + |
| 92 | +Use the official site `https://better-auth.com/docs` only to cross-reference — it tracks `main`, not the installed version. |
| 93 | + |
| 94 | +## Environment Variables |
| 95 | + |
| 96 | +Two env vars are resolved inside `create-context.ts` and `utils/url.ts`: |
| 97 | + |
| 98 | +- `BETTER_AUTH_SECRET` — required. At least 32 chars, high entropy. Generate with `openssl rand -base64 32` or `npx auth secret`. Also accepts `AUTH_SECRET`. For secret rotation without invalidating sessions, use `BETTER_AUTH_SECRETS` (plural, comma-separated). |
| 99 | +- `BETTER_AUTH_URL` — base URL of the app (e.g. `http://localhost:3000`). Public variants honored on clients: `NEXT_PUBLIC_BETTER_AUTH_URL`, `PUBLIC_BETTER_AUTH_URL`, `NUXT_PUBLIC_BETTER_AUTH_URL`. |
| 100 | + |
| 101 | +Missing or misconfigured values are the single most common source of runtime errors — see `references/common-errors.md`. |
| 102 | + |
| 103 | +## Server Setup (Canonical Pattern) |
| 104 | + |
| 105 | +Create `auth.ts` (project root, `lib/`, `utils/`, or a `src/`/`app/`/`server/`-nested equivalent) and export a named or default `auth`: |
| 106 | + |
| 107 | +```ts |
| 108 | +import { betterAuth } from "better-auth"; |
| 109 | + |
| 110 | +export const auth = betterAuth({ |
| 111 | + database: /* DB adapter or Kysely dialect — see references/databases.md */, |
| 112 | + emailAndPassword: { |
| 113 | + enabled: true, |
| 114 | + }, |
| 115 | + socialProviders: { |
| 116 | + // github: { clientId: env.GITHUB_CLIENT_ID, clientSecret: env.GITHUB_CLIENT_SECRET }, |
| 117 | + }, |
| 118 | + plugins: [ |
| 119 | + // see references/plugins.md |
| 120 | + ], |
| 121 | +}); |
| 122 | +``` |
| 123 | + |
| 124 | +The default export `betterAuth` lives in `packages/better-auth/src/auth/full.ts` and ships the full Kysely runtime. For environments where Kysely is not desired, import from `better-auth/minimal` and pass a custom adapter (e.g. `drizzleAdapter`) via `database`. |
| 125 | + |
| 126 | +## Client Setup (Canonical Pattern) |
| 127 | + |
| 128 | +Pick the framework-specific import path. Each one re-exports `createAuthClient` from `packages/better-auth/src/client/<framework>/index.ts`: |
| 129 | + |
| 130 | +```ts |
| 131 | +import { createAuthClient } from "better-auth/react"; // React |
| 132 | +import { createAuthClient } from "better-auth/vue"; // Vue / Nuxt |
| 133 | +import { createAuthClient } from "better-auth/svelte"; // Svelte / SvelteKit |
| 134 | +import { createAuthClient } from "better-auth/solid"; // Solid / Solid Start |
| 135 | +import { createAuthClient } from "better-auth/client"; // vanilla (framework-agnostic) |
| 136 | + |
| 137 | +export const authClient = createAuthClient({ |
| 138 | + baseURL: process.env.NEXT_PUBLIC_BETTER_AUTH_URL, // or equivalent per framework |
| 139 | + plugins: [ |
| 140 | + // mirror any server-side plugins that expose a client plugin |
| 141 | + ], |
| 142 | +}); |
| 143 | +``` |
| 144 | + |
| 145 | +Export the methods the app actually uses (`signIn`, `signUp`, `signOut`, `useSession`) from the client module rather than re-importing the whole `authClient` in every component. |
| 146 | + |
| 147 | +## Picking the Framework Adapter |
| 148 | + |
| 149 | +| Framework | Import | Mount | |
| 150 | +|-----------|--------|-------| |
| 151 | +| Next.js App Router | `better-auth/next-js` | `toNextJsHandler(auth)` at `app/api/auth/[...all]/route.ts` | |
| 152 | +| Next.js Pages Router | `better-auth/node` | `toNodeHandler(auth.handler)` + disable `bodyParser` | |
| 153 | +| SvelteKit | `better-auth/svelte-kit` | `svelteKitHandler({ event, resolve, auth, building })` in `hooks.server.ts` | |
| 154 | +| Solid Start | `better-auth/solid-start` | `toSolidStartHandler(auth)` | |
| 155 | +| TanStack Start | `better-auth/tanstack-start` | Mount `auth.handler`; use `tanstackStartCookies` plugin | |
| 156 | +| Nuxt / Nitro | (no source-level helper) | `auth.handler(toWebRequest(event))` in `server/api/auth/[...all].ts` | |
| 157 | +| Hono | (no source-level helper) | `auth.handler(c.req.raw)` at `/api/auth/*` | |
| 158 | +| Astro | (no source-level helper) | `auth.handler(ctx.request)` at `pages/api/auth/[...all].ts` | |
| 159 | +| Express | `better-auth/node` | `app.all("/api/auth/*", toNodeHandler(auth))` | |
| 160 | +| Bun | `better-auth/node` or native `Bun.serve` | Forward to `auth.handler` | |
| 161 | + |
| 162 | +Full wiring, cookie handling, and CORS gotchas per framework: `references/adapters.md`. |
| 163 | + |
| 164 | +## Picking Plugins |
| 165 | + |
| 166 | +Server plugins live under `packages/better-auth/src/plugins/`. Enumerate them before writing imports. Common picks: |
| 167 | + |
| 168 | +| Need | Plugin | Server import | Client import | |
| 169 | +|------|--------|---------------|---------------| |
| 170 | +| Multi-tenant / teams | `organization` | `better-auth/plugins/organization` | `better-auth/client/plugins` | |
| 171 | +| 2FA (TOTP / OTP / backup codes) | `two-factor` | `better-auth/plugins/two-factor` | same | |
| 172 | +| Passwordless magic link | `magic-link` | `better-auth/plugins/magic-link` | same | |
| 173 | +| WebAuthn / Passkeys | `passkey` (shipped in `@better-auth/passkey`) | `@better-auth/passkey` | `@better-auth/passkey/client` | |
| 174 | +| Dev-friendly OAuth over tunnels | `oauth-proxy` | `better-auth/plugins/oauth-proxy` | — | |
| 175 | +| JWT session tokens | `jwt` | `better-auth/plugins/jwt` | — | |
| 176 | +| OTP over email / SMS | `email-otp`, `phone-number` | `better-auth/plugins/email-otp` | same | |
| 177 | +| Admin / user management UI | `admin` | `better-auth/plugins/admin` | same | |
| 178 | + |
| 179 | +Full matrix with decision notes: `references/plugins.md`. |
| 180 | + |
| 181 | +## Picking the DB Adapter |
| 182 | + |
| 183 | +| Choice | Adapter | Import | |
| 184 | +|--------|---------|--------| |
| 185 | +| Prisma | `prismaAdapter(prisma, { provider })` | `better-auth/adapters/prisma` | |
| 186 | +| Drizzle | `drizzleAdapter(db, { provider })` | `better-auth/adapters/drizzle` | |
| 187 | +| Kysely | `kyselyAdapter(db, { type })` | `better-auth/adapters/kysely` | |
| 188 | +| MongoDB | `mongodbAdapter(client)` | `better-auth/adapters/mongodb` | |
| 189 | +| Memory (tests only) | `memoryAdapter(store)` | `better-auth/adapters/memory` | |
| 190 | +| Built-in Kysely dialect | pass dialect directly via `database` | — | |
| 191 | + |
| 192 | +After adding a plugin or changing the schema, regenerate: |
| 193 | + |
| 194 | +```bash |
| 195 | +npx auth generate # dumps schema changes |
| 196 | +npx auth migrate # applies them (Kysely built-in only) |
| 197 | +``` |
| 198 | + |
| 199 | +Full init snippets and the schema-extension pattern: `references/databases.md`. |
| 200 | + |
| 201 | +## When Typecheck or Runtime Fails |
| 202 | + |
| 203 | +Before searching source code, grep `references/common-errors.md` for the failing symptom (missing secret, redirect loop, CORS, session not persisted, schema drift, edge-runtime incompatibility). Most runtime failures fall into one of six known categories with a canonical fix. |
| 204 | + |
| 205 | +If the symptom is not listed, resolve the source and grep the error string: |
| 206 | + |
| 207 | +```bash |
| 208 | +rg -n "error string fragment" "$SRC/packages/better-auth/src" |
| 209 | +``` |
| 210 | + |
| 211 | +## References |
| 212 | + |
| 213 | +- [`references/adapters.md`](references/adapters.md) — framework-by-framework wiring (Next.js, Nuxt, SvelteKit, Hono, Astro, Express, Bun, TanStack Start, Solid Start) |
| 214 | +- [`references/plugins.md`](references/plugins.md) — every built-in plugin with when-to-use, server import, client import |
| 215 | +- [`references/databases.md`](references/databases.md) — Prisma / Drizzle / Kysely / Mongo init, schema generation, model extension |
| 216 | +- [`references/common-errors.md`](references/common-errors.md) — six concrete error → cause → fix entries |
0 commit comments