Awesome-omni-skill better-auth
TypeScript authentication framework (framework-agnostic). Features: email/password, OAuth (Google, GitHub, Discord), 2FA (TOTP, SMS), passkeys/WebAuthn, session management, RBAC, rate limiting, database adapters. Actions: implement, configure, secure authentication systems. Keywords: Better Auth, authentication, authorization, OAuth, email/password, 2FA, MFA, TOTP, passkeys, WebAuthn, session management, RBAC, rate limiting, database adapter, TypeScript auth, social login, Google auth, GitHub auth, Discord auth, email verification, password reset. Use when: implementing TypeScript auth, adding OAuth providers, setting up 2FA/MFA, managing sessions, configuring RBAC, building secure auth systems.
git clone https://github.com/diegosouzapw/awesome-omni-skill
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/better-auth-hsnsaboor" ~/.claude/skills/diegosouzapw-awesome-omni-skill-better-auth-a7ca45 && rm -rf "$T"
skills/development/better-auth-hsnsaboor/SKILL.mdBetter Auth
Better Auth is a comprehensive, framework-agnostic authentication/authorization framework for TypeScript with built-in email/password, social OAuth, and powerful plugin ecosystem for advanced features.
Always consult better-auth.com/docs for code examples and latest API.
When to Use
- Implementing auth in TypeScript/JavaScript applications
- Adding email/password or social OAuth authentication
- Setting up 2FA, passkeys, magic links, advanced auth features
- Building multi-tenant apps with organization support
- Managing sessions and user lifecycle
- Working with any framework (Next.js, Nuxt, SvelteKit, Remix, Astro, Hono, Express, etc.)
Planning Phase (Before Implementation)
Before writing any code, gather requirements by scanning the project and asking the user structured questions.
Step 1: Scan the project
Analyze the codebase to auto-detect:
- Framework — Look for
,next.config
,svelte.config
,nuxt.config
,astro.config
, or Express/Hono entry files.vite.config - Database/ORM — Look for
,prisma/schema.prisma
,drizzle.config
deps (package.json
,pg
,mysql2
,better-sqlite3
,mongoose
).mongodb - Existing auth — Look for existing auth libraries (
,next-auth
,lucia
,clerk
,supabase/auth
) infirebase/auth
or imports.package.json - Package manager — Check for
,pnpm-lock.yaml
,yarn.lock
, orbun.lockb
.package-lock.json
Use what you find to pre-fill defaults and skip questions you can already answer.
Step 2: Ask planning questions
Ask the user all applicable questions (skip any already answered by the scan):
- Project type — New project | Adding auth to existing project | Migrating from another auth library
- Framework — Next.js (App/Pages Router) | SvelteKit | Nuxt | Astro | Express | Hono | SolidStart | Other
- Database & ORM — PostgreSQL/MySQL/SQLite + Prisma/Drizzle/direct driver | MongoDB
- Authentication methods (allow multiple) — Email & password | Social OAuth | Magic link | Passkey | Phone number
- Social providers (if OAuth selected) — Google | GitHub | Apple | Microsoft | Discord | Twitter/X
- Email verification (if email/password selected) — Yes | No
- Email provider (if verification or password reset needed) — Resend | Mock (console.log)
- Features & plugins (allow multiple) — 2FA | Organizations/teams | Admin dashboard | API bearer tokens | Password reset | None
- Auth pages — Sign in | Sign up | Forgot password | Reset password | Email verification
- Auth UI style — Minimal & clean | Centered card | Split layout | Floating/glassmorphism | Custom
Step 3: Summarize and confirm the plan
Present a concise implementation plan as a markdown checklist. Get user confirmation before proceeding.
Decision Tree
Is this a new/empty project? ├─ YES → New project setup │ 1. Identify framework │ 2. Choose database │ 3. Install better-auth │ 4. Create auth.ts + auth-client.ts │ 5. Set up route handler │ 6. Run CLI migrate/generate │ 7. Add features via plugins │ ├─ MIGRATING → Migration from existing auth │ 1. Audit current auth for gaps │ 2. Plan incremental migration │ 3. Install better-auth alongside existing auth │ 4. Migrate routes, then session logic, then UI │ 5. Remove old auth library │ 6. See migration guides in docs │ └─ NO → Add auth to existing project 1. Analyze project structure 2. Install better-auth 3. Create auth config 4. Add route handler 5. Run schema migrations 6. Integrate into existing pages 7. Add planned plugins and features
Quick Start
Installation
npm install better-auth # or pnpm/yarn/bun add better-auth
Scoped packages (as needed):
| Package | Use case |
|---|---|
| WebAuthn/Passkey auth |
| SAML/OIDC enterprise SSO |
| Stripe payments |
| SCIM user provisioning |
| React Native/Expo |
Environment Variables
BETTER_AUTH_SECRET=<32+ chars, generate with: openssl rand -base64 32> BETTER_AUTH_URL=http://localhost:3000 DATABASE_URL=<your database connection string>
Add OAuth secrets as needed:
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, GOOGLE_CLIENT_ID, etc.
Only define
baseURL/secret in config if env vars are NOT set.
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.
File Location
CLI looks for
auth.ts in: ./, ./lib, ./utils, or under ./src. Use --config for custom path.
Server Config (auth.ts)
Location:
lib/auth.ts or src/lib/auth.ts
import { betterAuth } from "better-auth"; export const auth = betterAuth({ database: { // See Database section below }, emailAndPassword: { enabled: true, autoSignIn: true }, socialProviders: { github: { clientId: process.env.GITHUB_CLIENT_ID!, clientSecret: process.env.GITHUB_CLIENT_SECRET!, } } }); // Export types export type Session = typeof auth.$Infer.Session;
Core Config Options
| Option | Notes |
|---|---|
| Optional display name |
| Only if not set |
| Default . Set for root. |
| Only if not set |
| Required for most features. See adapters docs. |
| Redis/KV for sessions & rate limits |
| to activate |
| |
| Array of plugins |
| CSRF whitelist |
Minimal config needs:
database + emailAndPassword: { enabled: true }
Standard config adds: socialProviders, emailVerification.sendVerificationEmail, emailAndPassword.sendResetPassword
Full config adds: plugins, session, account.accountLinking, rateLimit
Client Config (auth-client.ts)
import { createAuthClient } from "better-auth/client"; export const authClient = createAuthClient({ baseURL: process.env.NEXT_PUBLIC_BETTER_AUTH_URL || "http://localhost:3000" });
Import by framework:
| Framework | Import |
|---|---|
| React/Next.js | |
| Vue | |
| Svelte | |
| Solid | |
| Vanilla JS | |
Client plugins go in
createAuthClient({ plugins: [...] }).
Common exports:
signIn, signUp, signOut, useSession, getSession
Route Handler Setup
| Framework | File | Handler |
|---|---|---|
| Next.js App Router | | → export |
| Next.js Pages | | → default export |
| Express | Any file | |
| SvelteKit | | |
| SolidStart | Route file | |
| Hono | Route file | |
Next.js Server Components: Add
nextCookies() plugin to auth config.
Database
Direct Connections
Pass
pg.Pool, mysql2 pool, better-sqlite3, or bun:sqlite instance directly.
ORM Adapters
Import from
better-auth/adapters/drizzle, better-auth/adapters/prisma, better-auth/adapters/mongodb.
| Database | Setup |
|---|---|
| SQLite | Pass or instance directly |
| PostgreSQL | Pass instance directly |
| MySQL | Pass pool directly |
| Prisma | from |
| Drizzle | from |
| MongoDB | from |
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".
Database Migrations
| Adapter | Command |
|---|---|
| Built-in Kysely | (applies directly) |
| Prisma | then |
| Drizzle | then |
Re-run after adding plugins.
Basic Usage
// Sign up await authClient.signUp.email({ email: "user@example.com", password: "secure123", name: "John Doe" }); // Sign in await authClient.signIn.email({ email: "user@example.com", password: "secure123" }); // OAuth await authClient.signIn.social({ provider: "github" }); // Session (client) const { data: session } = authClient.useSession(); // React/Vue/Svelte const { data: session } = await authClient.getSession(); // Vanilla JS // Session (server) const session = await auth.api.getSession({ headers: await headers() });
Session Management
Storage priority:
- If
defined → sessions go there (not DB)secondaryStorage - Set
to also persist to DBsession.storeSessionInDatabase: true - No database +
→ fully stateless modecookieCache
Cookie cache strategies:
(default) - Base64url + HMAC. Smallest.compact
- Standard JWT. Readable but signed.jwt
- Encrypted. Maximum security.jwe
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
- Must be defined for verification to workemailVerification.sendVerificationEmail
/emailVerification.sendOnSignUp
- Auto-send triggerssendOnSignIn
- Password reset email handleremailAndPassword.sendResetPassword
Feature Selection Matrix
| Feature | Plugin Required | Use Case | Reference |
|---|---|---|---|
| Email/Password | No (built-in) | Basic auth | email-password-auth.md |
| OAuth (GitHub, Google, etc.) | No (built-in) | Social login | oauth-providers.md |
| Email Verification | No (built-in) | Verify email addresses | email-password-auth.md |
| Password Reset | No (built-in) | Forgot password flow | email-password-auth.md |
| Two-Factor Auth (2FA/TOTP) | Yes () | Enhanced security | advanced-features.md |
| Passkeys/WebAuthn | Yes () | Passwordless auth | advanced-features.md |
| Magic Link | Yes () | Email-based login | advanced-features.md |
| Username Auth | Yes () | Username login | email-password-auth.md |
| Organizations/Multi-tenant | Yes () | Team/org features | advanced-features.md |
| Rate Limiting | No (built-in) | Prevent abuse | advanced-features.md |
| Session Management | No (built-in) | User sessions | advanced-features.md |
Auth Method Selection Guide
Choose Email/Password when:
- Building standard web app with traditional auth
- Need full control over user credentials
- Targeting users who prefer email-based accounts
Choose OAuth when:
- Want quick signup with minimal friction
- Users already have social accounts
- Need access to social profile data
Choose Passkeys when:
- Want passwordless experience
- Targeting modern browsers/devices
- Security is top priority
Choose Magic Link when:
- Want passwordless without WebAuthn complexity
- Targeting email-first users
- Need temporary access links
Combine Multiple Methods when:
- Want flexibility for different user preferences
- Building enterprise apps with various auth requirements
- Need progressive enhancement (start simple, add more options)
Plugins
Import from dedicated paths for tree-shaking:
import { twoFactor } from "better-auth/plugins/two-factor"
NOT
from "better-auth/plugins".
Common Plugins
| Plugin | Server Import | Client Import | Purpose |
|---|---|---|---|
| | | 2FA with TOTP/OTP |
| | | Teams/orgs |
| | | User management |
| | - | API token auth |
| | - | API docs |
| | | WebAuthn |
| | - | Enterprise SSO |
Popular plugins:
twoFactor, organization, passkey, magicLink, emailOtp, username, phoneNumber, admin, apiKey, bearer, jwt, multiSession, sso, oauthProvider, oidcProvider, openAPI, genericOAuth.
Plugin pattern: Server plugin + client plugin + run migrations.
Client plugins go in
createAuthClient({ plugins: [...] }).
Security
Config (advanced
section)
advanced
- Force HTTPS cookiesuseSecureCookies
- Security riskdisableCSRFCheck
- Security riskdisableOriginCheck
- Share cookies across subdomainscrossSubDomainCookies.enabled
- Custom IP headers for proxiesipAddress.ipAddressHeaders
- Custom ID generation ordatabase.generateId
/"serial"
/"uuid"false
Rate limiting:
rateLimit.enabled, rateLimit.window, rateLimit.max, rateLimit.storage ("memory" | "database" | "secondary-storage").
Security Checklist
-
set (32+ chars)BETTER_AUTH_SECRET -
in productionadvanced.useSecureCookies: true -
configuredtrustedOrigins - Rate limits enabled
- Email verification enabled
- Password reset implemented
- 2FA for sensitive apps
- CSRF protection NOT disabled
-
reviewedaccount.accountLinking
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.
Type Safety
Infer types:
typeof auth.$Infer.Session, typeof auth.$Infer.Session.user.
For separate client/server projects:
createAuthClient<typeof auth>().
Auth UI Implementation
Sign in flow:
orsignIn.email({ email, password })signIn.social({ provider, callbackURL })- Handle
in responseerror - Redirect on success
Session check (client):
useSession() hook returns { data: session, isPending }
Session check (server):
auth.api.getSession({ headers: await headers() })
Protected routes: Check session, redirect to
/sign-in if null.
Core Architecture
Better Auth uses client-server architecture:
- Server (
): Handles auth logic, database ops, API routesbetter-auth - Client (
): Provides hooks/methods for frontendbetter-auth/client - Plugins: Extend both server/client functionality
Implementation Checklist
- Install
packagebetter-auth - Set environment variables (SECRET, URL)
- Create auth server instance with database config
- Run schema migration (
)npx @better-auth/cli generate - Mount API handler in framework
- Create client instance
- Implement sign-up/sign-in UI
- Add session management to components
- Set up protected routes/middleware
- Add plugins as needed (regenerate schema after)
- Test complete auth flow
- Configure email sending (verification/reset)
- Enable rate limiting for production
- Set up error handling
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
Troubleshooting
| Issue | Fix |
|---|---|
| "Secret not set" | Add env var |
| "Invalid Origin" | Add domain to |
| Cookies not setting | Check matches domain; enable secure cookies in prod |
| OAuth callback errors | Verify redirect URIs in provider dashboard |
| Type errors after adding plugin | Re-run CLI generate/migrate |
Reference Documentation
Core Authentication
- Email/Password Authentication - Email/password setup, verification, password reset, username auth
- OAuth Providers - Social login setup, provider configuration, token management
- Database Integration - Database adapters, schema setup, migrations
Advanced Features
- Advanced Features - 2FA/MFA, passkeys, magic links, organizations, rate limiting, session management
Scripts
- Initialize Better Auth configuration with interactive setupscripts/better_auth_init.py
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
- Plugins: https://better-auth.com/docs/plugins
- Examples: https://better-auth.com/docs/examples
- Init Options Source: https://github.com/better-auth/better-auth/blob/main/packages/core/src/types/init-options.ts