963 markdown files reformatted with the repository's pinned prettier so pnpm format:check covers the folded tree like every other repo file. The formatter's embedded-language pass also normalized code fences (TS semicolons, closed HTML tags in examples, lowercased CSS hex colors, one renumbered list that skipped an index). Alphanumeric token deltas vs the fold commit were audited file-by-file; all are formatter-equivalent markup normalizations plus the four sanitized skills.
219 lines
7.9 KiB
Markdown
219 lines
7.9 KiB
Markdown
---
|
|
name: create-auth-skill
|
|
description: Skill for creating auth layers in TypeScript/JavaScript apps using Better Auth.
|
|
---
|
|
|
|
# Create Auth Skill
|
|
|
|
Guide for adding authentication to TypeScript/JavaScript applications using Better Auth.
|
|
|
|
**For code examples and syntax, see [better-auth.com/docs](https://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
|
|
|
|
```env
|
|
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:**
|
|
|
|
1. `signIn.email({ email, password })` or `signIn.social({ provider, callbackURL })`
|
|
2. Handle `error` in response
|
|
3. 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](https://better-auth.com/docs)
|
|
- [Examples](https://github.com/better-auth/examples)
|
|
- [Plugins](https://better-auth.com/docs/concepts/plugins)
|
|
- [CLI](https://better-auth.com/docs/concepts/cli)
|
|
- [Migration Guides](https://better-auth.com/docs/guides)
|