Install
$ agentstack add skill-fellipeutaka-leon-better-auth ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README — it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming — see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps — measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Better Auth
Framework-agnostic TypeScript auth library. Plugin-based architecture, 40+ OAuth providers, 18+ framework integrations.
Quick Start
Install
npm install better-auth
Scoped packages (as needed):
| Package | Use case | |---------|----------| | @better-auth/passkey | WebAuthn/Passkey auth | | @better-auth/sso | SAML/OIDC enterprise SSO | | @better-auth/stripe | Stripe payments | | @better-auth/expo | React Native/Expo |
Environment Variables
BETTER_AUTH_SECRET=
BETTER_AUTH_URL=http://localhost:3000
DATABASE_URL=
Server Config (lib/auth.ts)
import { betterAuth } from "better-auth";
export const auth = betterAuth({
database: process.env.DATABASE_URL, // or adapter instance
emailAndPassword: { enabled: true },
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
},
plugins: [], // add plugins here
});
export type Session = typeof auth.$Infer.Session;
Client Config (lib/auth-client.ts)
import { createAuthClient } from "better-auth/react"; // or /vue, /svelte, /solid, /client
export const authClient = createAuthClient({
plugins: [], // add client plugins here
});
Route Handler
| Framework | File | Handler | |-----------|------|---------| | Next.js App Router | app/api/auth/[...all]/route.ts | toNextJsHandler(auth) → export { GET, POST } | | Next.js Pages | pages/api/auth/[...all].ts | toNextJsHandler(auth) → default export | | Express | any | app.all("/api/auth/*splat", toNodeHandler(auth)) | | Hono | route | app.on(["POST","GET"], "/api/auth/**", (c) => auth.handler(c.req.raw)) | | SvelteKit | hooks.server.ts | svelteKitHandler({ auth, event }) | | Astro | pages/api/auth/[...all].ts | toAstroHandler(auth) | | Elysia | plugin | new Elysia().mount(auth.handler) |
See [references/framework-integrations.md](references/framework-integrations.md) for all frameworks.
CLI Commands
npx @better-auth/cli@latest migrate # Apply schema (built-in adapter)
npx @better-auth/cli@latest generate # Generate for Prisma/Drizzle
npx @better-auth/cli@latest generate --output prisma/schema.prisma
npx @better-auth/cli@latest generate --output src/db/auth-schema.ts
Re-run after adding/changing plugins.
Core Concepts
- Server instance (
auth): handles all auth logic, DB, sessions - Client instance (
authClient): framework-specific hooks (useSession,signIn,signUp,signOut) - Plugins: extend both server and client — add endpoints, DB tables, hooks
- Type inference:
auth.$Infer.Session,auth.$Infer.Session.userfor full type safety - For separate client/server projects:
createAuthClient()
Authentication Methods
| Method | Package | Config/Plugin | Reference | |--------|---------|---------------|-----------| | Email/Password | built-in | emailAndPassword: { enabled: true } | [authentication.md](references/authentication.md) | | Social OAuth | built-in | socialProviders: { google: {...} } | [authentication.md](references/authentication.md) | | Magic Link | built-in | magicLink() plugin | [authentication.md](references/authentication.md) | | Passkey | @better-auth/passkey | passkey() plugin | [authentication.md](references/authentication.md) | | Username | built-in | username() plugin | [authentication.md](references/authentication.md) | | Email OTP | built-in | emailOtp() plugin | [authentication.md](references/authentication.md) | | Phone Number | built-in | phoneNumber() plugin | [authentication.md](references/authentication.md) | | Anonymous | built-in | anonymous() plugin | [authentication.md](references/authentication.md) |
Plugin Quick Reference
Import from dedicated paths for tree-shaking: import { twoFactor } from "better-auth/plugins/two-factor" NOT from "better-auth/plugins".
| Plugin | Server Import | Client Import | Purpose | |--------|---------------|---------------|---------| | twoFactor | better-auth/plugins/two-factor | twoFactorClient | TOTP, OTP, backup codes | | organization | better-auth/plugins/organization | organizationClient | Multi-tenant orgs, teams, RBAC | | admin | better-auth/plugins/admin | adminClient | User management, impersonation | | passkey | @better-auth/passkey | passkeyClient | WebAuthn/FIDO2 | | magicLink | better-auth/plugins/magic-link | magicLinkClient | Passwordless email links | | emailOtp | better-auth/plugins/email-otp | emailOtpClient | Email one-time passwords | | username | better-auth/plugins/username | usernameClient | Username-based auth | | phoneNumber | better-auth/plugins/phone-number | phoneNumberClient | Phone-based auth | | anonymous | better-auth/plugins/anonymous | anonymousClient | Guest sessions | | apiKey | better-auth/plugins/api-key | apiKeyClient | API key management | | bearer | better-auth/plugins/bearer | — | Bearer token auth | | jwt | better-auth/plugins/jwt | jwtClient | JWT tokens | | multiSession | better-auth/plugins/multi-session | multiSessionClient | Multiple active sessions | | oauthProvider | better-auth/plugins/oauth-provider | — | Become OAuth provider | | oidcProvider | better-auth/plugins/oidc-provider | — | Become OIDC provider | | sso | @better-auth/sso | ssoClient | SAML/OIDC enterprise SSO | | openAPI | better-auth/plugins/open-api | — | API documentation | | customSession | better-auth/plugins/custom-session | — | Extend session data | | genericOAuth | better-auth/plugins/generic-oauth | genericOAuthClient | Custom OAuth providers | | oneTap | better-auth/plugins/one-tap | oneTapClient | Google One Tap |
Pattern: server plugin in auth({ plugins: [...] }) + client plugin in createAuthClient({ plugins: [...] }) + re-run CLI migrations.
See [references/plugins.md](references/plugins.md) for detailed usage and custom plugin creation.
Database Setup
| Adapter | Setup | |---------|-------| | SQLite | Pass better-sqlite3 or bun:sqlite instance | | PostgreSQL | Pass pg.Pool instance | | MySQL | Pass mysql2 pool | | Prisma | prismaAdapter(prisma, { provider: "postgresql" }) from better-auth/adapters/prisma | | Drizzle | drizzleAdapter(db, { provider: "pg" }) from better-auth/adapters/drizzle | | MongoDB | mongodbAdapter(db) from better-auth/adapters/mongodb | | Connection string | database: process.env.DATABASE_URL (uses built-in Kysely) |
Critical: Config uses ORM model name, NOT DB table name. Prisma model User mapping to table users → use modelName: "user".
Core schema tables: user, session, account, verification. Plugins add their own tables.
See [references/setup.md](references/setup.md) for full database setup details.
Session Management
Key options:
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days (default)
updateAge: 60 * 60 * 24, // refresh every 24h (default)
freshAge: 60 * 60 * 24, // require re-auth after 24h for sensitive ops
cookieCache: {
enabled: true,
maxAge: 300, // 5 min
strategy: "compact", // "compact" | "jwt" | "jwe"
},
}
secondaryStorage(Redis/KV): sessions go there by default, not DB- Stateless mode: no DB + cookieCache = session in cookie only
customSessionplugin: extend session with custom fields
See [references/sessions.md](references/sessions.md) for full session management details.
Security Checklist
| DO | DON'T | |----|-------| | Use 32+ char secret with high entropy | Commit secrets to version control | | Set baseURL with HTTPS in production | Disable CSRF check (disableCSRFCheck) | | Configure trustedOrigins for all frontends | Disable origin check | | Enable rate limiting (on by default in prod) | Use "memory" rate limit storage in serverless | | Configure backgroundTasks.handler on serverless | Skip email verification setup | | Use "jwe" cookie cache for sensitive session data | Store OAuth tokens unencrypted if used for API calls | | Set revokeSessionsOnPasswordReset: true | Return specific error messages ("user not found") |
See [references/security.md](references/security.md) for complete security hardening guide.
Common Gotchas
- Model vs table name — config uses ORM model name, not DB table name
- Plugin schema — re-run CLI after adding/changing plugins
- Secondary storage — sessions go there by default, not DB. Set
session.storeSessionInDatabase: trueto persist both - Cookie cache — custom session fields NOT cached, always re-fetched from DB
- Callback URLs — always use absolute URLs with origin (not relative paths)
- Express v5 — use
"/api/auth/*splat"not"/api/auth/*"for catch-all routes - Next.js RSC — add
nextCookies()plugin to auth config for server component session access
Troubleshooting
| Issue | Fix | |-------|-----| | "Secret not set" | Add BETTER_AUTH_SECRET env var | | "Invalid Origin" | Add domain to trustedOrigins | | Cookies not setting | Check baseURL matches domain; enable secure cookies in prod | | OAuth callback errors | Verify redirect URIs in provider dashboard match exactly | | Type errors after adding plugin | Re-run CLI generate/migrate | | Session null in RSC | Add nextCookies() plugin | | 2FA redirect not working | Add twoFactorClient with onTwoFactorRedirect to client |
Reference Index
| File | When to read | |------|-------------| | [setup.md](references/setup.md) | Setting up new project, configuring DB, route handlers | | [authentication.md](references/authentication.md) | Implementing any auth method (email, social, passkey, magic link, etc.) | | [sessions.md](references/sessions.md) | Configuring session expiry, caching, stateless mode, secondary storage | | [security.md](references/security.md) | Hardening for production — rate limiting, CSRF, cookies, OAuth security | | [plugins.md](references/plugins.md) | Using or creating plugins, plugin catalog | | [framework-integrations.md](references/framework-integrations.md) | Framework-specific setup (Next.js, Nuxt, SvelteKit, Hono, Express, etc.) | | [two-factor.md](references/two-factor.md) | Implementing 2FA (TOTP, OTP, backup codes, trusted devices) | | [organizations.md](references/organizations.md) | Multi-tenant orgs, teams, invitations, RBAC | | [admin.md](references/admin.md) | User management, roles, banning, impersonation | | [hooks-and-middleware.md](references/hooks-and-middleware.md) | Custom logic via before/after hooks, DB hooks, middleware |
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: fellipeutaka
- Source: fellipeutaka/leon
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.