---
description: Better Auth conventions
globs: ["src/**/*.{ts,tsx}", "lib/auth*.ts", "app/**/*.{ts,tsx}"]
alwaysApply: false
---

# Better Auth

- One server instance in `lib/auth.ts`: `export const auth = betterAuth({ database: <adapter>, emailAndPassword: {...}, socialProviders: {...}, plugins: [...] })`. One client in `lib/auth-client.ts` via `createAuthClient()` from the framework entry (`better-auth/react`, `/vue`, `/svelte`).
- Secrets from env only: `BETTER_AUTH_SECRET`, `BETTER_AUTH_URL`, provider client IDs/secrets. Never hard-code or log them, and never import `lib/auth.ts` into client components.
- Mount the handler once (e.g. Next.js `app/api/auth/[...all]/route.ts` with `toNextJsHandler(auth)`; Hono/Express with `auth.handler`).
- Schema: after adding plugins or fields run the CLI (`npx @better-auth/cli generate` for ORM schemas, or `migrate` for the built-in Kysely adapter), then the ORM's migration. Don't hand-edit auth tables.
- Authorization is enforced on the server: `const session = await auth.api.getSession({ headers })` in route handlers, server actions and loaders; return 401/redirect when missing. Middleware cookie checks are only an optimistic UX redirect, not security.
- Check ownership/roles for every resource access (e.g. with the `admin` or `organization` plugin roles); a valid session isn't permission.
- Add plugins (twoFactor, passkey, organization, magicLink) on both server and client (`...Client()` plugin) so types line up.
- Configure `trustedOrigins` for every frontend origin; keep cookies `secure` in production.
- Rate limiting stays enabled for sign-in, sign-up and password reset; send verification and reset emails from a server-side mailer.

## Done when
Typecheck and build pass, migrations are generated and committed, and sign-up, sign-in, sign-out and a protected route work end to end.
