Create Auth Skill
Guide for adding authentication to TypeScript/JavaScript applications using Better Auth.
For code examples and syntax, see better-auth.com/docs.
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 │ └─ NO → Does project have existing auth? ├─ YES → Migration/enhancement │ • Audit current auth for gaps │ • Plan incremental migration │ • 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
Installation
Core: 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/scim
SCIM user provisioning
@better-auth/expo
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.
Server Config (auth.ts)
Location: lib/auth.ts or src/lib/auth.ts
Minimal config needs:
-
database
-
Connection or adapter
-
emailAndPassword: { enabled: true }
-
For email/password auth
Standard config adds:
-
socialProviders
-
OAuth providers (google, github, etc.)
-
emailVerification.sendVerificationEmail
-
Email verification handler
-
emailAndPassword.sendResetPassword
-
Password reset handler
Full config adds:
-
plugins
-
Array of feature plugins
-
session
-
Expiry, cookie cache settings
-
account.accountLinking
-
Multi-provider linking
-
rateLimit
-
Rate limiting config
Export types: export type Session = typeof auth.$Infer.Session
Client Config (auth-client.ts)
Import by framework:
Framework Import
React/Next.js better-auth/react
Vue better-auth/vue
Svelte better-auth/svelte
Solid better-auth/solid
Vanilla JS better-auth/client
Client plugins go in createAuthClient({ plugins: [...] }) .
Common exports: signIn , signUp , signOut , useSession , getSession
Route Handler Setup
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 file app.all("/api/auth/*", toNodeHandler(auth))
SvelteKit src/hooks.server.ts
svelteKitHandler(auth)
SolidStart Route file solidStartHandler(auth)
Hono Route file auth.handler(c.req.raw)
Next.js Server Components: Add nextCookies() plugin to auth config.
Database Migrations
Adapter Command
Built-in Kysely npx @better-auth/cli@latest migrate (applies directly)
Prisma npx @better-auth/cli@latest generate --output prisma/schema.prisma then npx prisma migrate dev
Drizzle npx @better-auth/cli@latest generate --output src/db/auth-schema.ts then npx drizzle-kit push
Re-run after adding plugins.
Database Adapters
Database Setup
SQLite Pass better-sqlite3 or bun:sqlite instance directly
PostgreSQL Pass pg.Pool instance directly
MySQL Pass mysql2 pool directly
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
Common Plugins
Plugin Server Import Client Import Purpose
twoFactor
better-auth/plugins
twoFactorClient
2FA with TOTP/OTP
organization
better-auth/plugins
organizationClient
Teams/orgs
admin
better-auth/plugins
adminClient
User management
bearer
better-auth/plugins
API token auth
openAPI
better-auth/plugins
API docs
passkey
@better-auth/passkey
passkeyClient
WebAuthn
sso
@better-auth/sso
Enterprise SSO
Plugin pattern: Server plugin + client plugin + run migrations.
Auth UI Implementation
Sign in flow:
-
signIn.email({ email, password }) or signIn.social({ provider, callbackURL })
-
Handle error in response
-
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.
Security Checklist
-
BETTER_AUTH_SECRET set (32+ chars)
-
advanced.useSecureCookies: true in production
-
trustedOrigins configured
-
Rate limits enabled
-
Email verification enabled
-
Password reset implemented
-
2FA for sensitive apps
-
CSRF protection NOT disabled
-
account.accountLinking reviewed
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
Type errors after adding plugin Re-run CLI generate/migrate
Resources
-
Docs
-
Examples
-
Plugins
-
CLI
-
Migration Guides