# Better Auth Plugin Compass This note is the project-level compass for choosing Better Auth plugins. It combines the current official plugin ecosystem, installed package evidence, and `cfw-auth`'s Cloudflare/D1 constraints. ## Source Baseline - Better Auth official plugin overview, v1.6: `https://better-auth.com/docs/plugins` - Better Auth plugin mechanism docs: `https://better-auth.com/docs/concepts/plugins` - Official plugin pages for 2FA, passkey, organization, API key, JWT, Stripe, and community plugins - Installed package type definitions under `node_modules/better-auth` and `node_modules/@better-auth/*` - Local runtime config: `src/plugins.ts` - Local schema-generation config: `src/auth.migration.ts` Important rule: official docs establish product intent; installed package types establish the exact API shape this repo can compile against. ## Ecosystem Map | Category | Plugins | Primary use | | --- | --- | --- | | Login and identity proof | `twoFactor`, `passkey`, `magicLink`, `emailOTP`, `phoneNumber`, `anonymous`, `username`, `oneTap`, `siwe`, `genericOAuth`, `multiSession`, `lastLoginMethod` | Human authentication, passwordless flows, account recovery, session UX | | Authorization and management | `admin`, `organization`, `sso`, `scim` | Admin controls, B2B tenants, enterprise SSO, directory sync | | API, token, and machine access | `agentAuth`, `apiKey`, `jwt`, `bearer`, `oneTimeToken`, `oauthProxy` | API clients, service access, token exchange, agent flows | | Identity-provider surface | `oauthProvider`, `oidcProvider`, `mcp`, `deviceAuthorization` | Turning the app into an OAuth/OIDC/MCP identity provider | | Billing and entitlement | `stripe`, `polar`, `autumn`, `creem`, `dodopayments`, `commet` | Subscription, checkout, seats, usage-based pricing | | Security and operations | `captcha`, `haveIBeenPwned`, `i18n`, `openAPI`, `testUtils` | Abuse prevention, password safety, localized errors, docs, tests | | Analytics and tracking | `dub` | Lead tracking and OAuth/link attribution | Community plugins are useful for ecosystem gaps such as LDAP, audit logs, Firebase Auth, payment providers, wallet chains, university email checks, or devtools. Treat them as third-party code: review maintenance, schema changes, runtime compatibility, and test coverage before adopting them. ## `cfw-auth` Current Plugin Set Default runtime plugins: - `openAPI` - `i18n` - `haveIBeenPwned` - `emailOTP` - `twoFactor` - `multiSession` - `lastLoginMethod` - `admin` - `username` - `phoneNumber` - `organization` - `apiKey` - `expo` Conditional runtime plugins: - `captcha`, when `CAPTCHA_PROVIDER=cloudflare-turnstile` and `CAPTCHA_SECRET_KEY` are set - `genericOAuth`, when generic OAuth provider env vars are set - `passkey`, when `PASSKEY_RP_ID`, `PASSKEY_RP_NAME`, and `PASSKEY_ORIGIN` are set - `jwt`, when `ENABLE_JWT=true` - `bearer`, when `ENABLE_BEARER=true` Migration-only static config must include every plugin that can affect schema or generated endpoint metadata. In this repo that means `src/auth.migration.ts` intentionally includes dummy config for conditional plugins. The important schema-bearing addition is `jwt`, which creates the `jwks` table. ## Usage Patterns Server config: ```ts import { betterAuth } from "better-auth"; import { i18n } from "@better-auth/i18n"; import { passkey } from "@better-auth/passkey"; import { jwt, organization, twoFactor } from "better-auth/plugins"; export const auth = betterAuth({ database: env.DB, plugins: [ i18n({ translations: { en: {}, zh: { USER_NOT_FOUND: "用户不存在。" }, }, detection: ["header", "cookie"], }), organization(), twoFactor(), passkey(), jwt(), ], }); ``` Client config, when a plugin exposes a client plugin: ```ts import { createAuthClient } from "better-auth/client"; import { i18nClient } from "@better-auth/i18n/client"; import { twoFactorClient } from "better-auth/client/plugins"; export const authClient = createAuthClient({ plugins: [ i18nClient(), twoFactorClient(), ], }); ``` ## Best Practices - Choose plugins from workflow pressure, not from the catalog. Every plugin can add endpoints, hooks, schema, client actions, and security review surface. - Keep server and client plugin choices paired. Some server plugins need a matching client plugin for typed client actions. - After changing schema-affecting plugins, run `pnpm db:generate`, inspect SQL, then run `pnpm db:check`. - Keep conditional runtime plugins represented in `src/auth.migration.ts` when they can affect schema or generated API shape. - Do not treat `jwt` as a session replacement. Use it for services that need JWT/JWKS, and keep cookie sessions as the normal browser auth path. - Use `bearer` only for clients that must send bearer tokens to auth endpoints. It widens token-acceptance behavior. - Keep `captcha` on high-abuse endpoints such as email sign-up, email sign-in, and password reset. Turnstile secrets belong in Worker secrets. - Keep `genericOAuth` provider config behind env and avoid hard-coding enterprise IdP details. - Treat `organization` as the tenant boundary. App tables should consume organization/member projections rather than becoming a second source of auth truth. - Keep API keys hashed, named, prefixed, scoped, and revocable. Organization-owned keys are better for shared integrations. - Review community plugins like any third-party dependency: schema, hooks, secrets, runtime assumptions, release activity, and tests. ## Simplicity Boundary The stable core for this project is: ```text User + Session + Identifier + Organization + Membership + API Key ``` Everything else is an edge capability: - `captcha`, `haveIBeenPwned`, and `i18n` are auth-flow protection and presentation edges. - `genericOAuth`, `passkey`, `phoneNumber`, and `emailOTP` are identity proof adapters. - `jwt`, `bearer`, and `apiKey` are machine/API access edges. - `expo` is client runtime integration. Do not move billing, enterprise SSO, SCIM, MCP provider, or OAuth/OIDC provider concerns into the core until a real caller needs them.