@kood/claude-code 0.3.6 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/.claude/agents/code-reviewer.md +124 -124
- package/templates/.claude/agents/dependency-manager.md +85 -85
- package/templates/.claude/agents/deployment-validator.md +56 -56
- package/templates/.claude/agents/git-operator.md +64 -64
- package/templates/.claude/agents/implementation-executor.md +95 -95
- package/templates/.claude/agents/ko-to-en-translator.md +74 -0
- package/templates/.claude/agents/lint-fixer.md +78 -78
- package/templates/.claude/agents/refactor-advisor.md +122 -122
- package/templates/.claude/commands/agent-creator.md +185 -185
- package/templates/.claude/commands/bug-fix.md +193 -193
- package/templates/.claude/commands/command-creator.md +54 -54
- package/templates/.claude/commands/docs-creator.md +57 -57
- package/templates/.claude/commands/docs-refactor.md +26 -26
- package/templates/.claude/commands/execute.md +12 -12
- package/templates/.claude/commands/git-all.md +32 -32
- package/templates/.claude/commands/git-session.md +42 -42
- package/templates/.claude/commands/git.md +34 -34
- package/templates/.claude/commands/lint-fix.md +138 -138
- package/templates/.claude/commands/lint-init.md +61 -61
- package/templates/.claude/commands/plan.md +260 -260
- package/templates/.claude/commands/prd.md +24 -24
- package/templates/.claude/commands/pre-deploy.md +109 -109
- package/templates/.claude/commands/refactor.md +147 -147
- package/templates/.claude/commands/version-update.md +17 -17
- package/templates/hono/CLAUDE.md +27 -27
- package/templates/hono/docs/architecture.md +24 -24
- package/templates/hono/docs/deployment/cloudflare.md +18 -18
- package/templates/hono/docs/deployment/docker.md +13 -13
- package/templates/hono/docs/deployment/index.md +19 -19
- package/templates/hono/docs/deployment/railway.md +32 -32
- package/templates/hono/docs/deployment/vercel.md +29 -29
- package/templates/hono/docs/guides/conventions.md +57 -57
- package/templates/hono/docs/guides/env-setup.md +47 -47
- package/templates/hono/docs/guides/getting-started.md +27 -27
- package/templates/hono/docs/library/hono/error-handling.md +11 -11
- package/templates/hono/docs/library/hono/index.md +4 -4
- package/templates/hono/docs/library/hono/middleware.md +18 -18
- package/templates/hono/docs/library/hono/rpc.md +7 -7
- package/templates/hono/docs/library/hono/validation.md +6 -6
- package/templates/hono/docs/library/prisma/cloudflare-d1.md +29 -29
- package/templates/hono/docs/library/prisma/config.md +16 -16
- package/templates/hono/docs/library/prisma/index.md +32 -32
- package/templates/hono/docs/library/t3-env/index.md +22 -22
- package/templates/hono/docs/library/zod/index.md +31 -31
- package/templates/nextjs/CLAUDE.md +228 -0
- package/templates/nextjs/docs/design.md +558 -0
- package/templates/nextjs/docs/guides/conventions.md +343 -0
- package/templates/nextjs/docs/guides/getting-started.md +367 -0
- package/templates/nextjs/docs/guides/routes.md +342 -0
- package/templates/nextjs/docs/library/better-auth/index.md +541 -0
- package/templates/nextjs/docs/library/nextjs/app-router.md +269 -0
- package/templates/nextjs/docs/library/nextjs/caching.md +351 -0
- package/templates/nextjs/docs/library/nextjs/index.md +291 -0
- package/templates/nextjs/docs/library/nextjs/middleware.md +391 -0
- package/templates/nextjs/docs/library/nextjs/route-handlers.md +382 -0
- package/templates/nextjs/docs/library/nextjs/server-actions.md +366 -0
- package/templates/nextjs/docs/library/prisma/cloudflare-d1.md +76 -0
- package/templates/nextjs/docs/library/prisma/config.md +77 -0
- package/templates/nextjs/docs/library/prisma/crud.md +90 -0
- package/templates/nextjs/docs/library/prisma/index.md +73 -0
- package/templates/nextjs/docs/library/prisma/relations.md +69 -0
- package/templates/nextjs/docs/library/prisma/schema.md +98 -0
- package/templates/nextjs/docs/library/prisma/setup.md +49 -0
- package/templates/nextjs/docs/library/prisma/transactions.md +50 -0
- package/templates/nextjs/docs/library/tanstack-query/index.md +66 -0
- package/templates/nextjs/docs/library/tanstack-query/invalidation.md +54 -0
- package/templates/nextjs/docs/library/tanstack-query/optimistic-updates.md +77 -0
- package/templates/nextjs/docs/library/tanstack-query/use-mutation.md +63 -0
- package/templates/nextjs/docs/library/tanstack-query/use-query.md +70 -0
- package/templates/nextjs/docs/library/zod/complex-types.md +61 -0
- package/templates/nextjs/docs/library/zod/index.md +56 -0
- package/templates/nextjs/docs/library/zod/transforms.md +51 -0
- package/templates/nextjs/docs/library/zod/validation.md +70 -0
- package/templates/npx/CLAUDE.md +37 -37
- package/templates/npx/docs/library/commander/index.md +12 -12
- package/templates/npx/docs/library/fs-extra/index.md +9 -9
- package/templates/npx/docs/library/prompts/index.md +3 -3
- package/templates/npx/docs/references/patterns.md +12 -12
- package/templates/tanstack-start/CLAUDE.md +53 -49
- package/templates/tanstack-start/docs/architecture.md +128 -128
- package/templates/tanstack-start/docs/design.md +169 -169
- package/templates/tanstack-start/docs/guides/conventions.md +43 -43
- package/templates/tanstack-start/docs/guides/env-setup.md +35 -35
- package/templates/tanstack-start/docs/guides/getting-started.md +19 -19
- package/templates/tanstack-start/docs/guides/hooks.md +63 -35
- package/templates/tanstack-start/docs/guides/routes.md +61 -42
- package/templates/tanstack-start/docs/guides/services.md +45 -45
- package/templates/tanstack-start/docs/library/better-auth/index.md +68 -68
- package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +19 -19
- package/templates/tanstack-start/docs/library/prisma/config.md +16 -16
- package/templates/tanstack-start/docs/library/prisma/crud.md +17 -17
- package/templates/tanstack-start/docs/library/prisma/index.md +17 -17
- package/templates/tanstack-start/docs/library/prisma/relations.md +16 -16
- package/templates/tanstack-start/docs/library/prisma/schema.md +23 -23
- package/templates/tanstack-start/docs/library/prisma/setup.md +6 -6
- package/templates/tanstack-start/docs/library/prisma/transactions.md +10 -10
- package/templates/tanstack-start/docs/library/t3-env/index.md +21 -160
- package/templates/tanstack-start/docs/library/tanstack-query/index.md +6 -6
- package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +19 -19
- package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +4 -4
- package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +14 -14
- package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +21 -21
- package/templates/tanstack-start/docs/library/tanstack-router/error-handling.md +9 -9
- package/templates/tanstack-start/docs/library/tanstack-router/hooks.md +11 -11
- package/templates/tanstack-start/docs/library/tanstack-router/index.md +18 -18
- package/templates/tanstack-start/docs/library/tanstack-router/navigation.md +17 -17
- package/templates/tanstack-start/docs/library/tanstack-router/route-context.md +5 -5
- package/templates/tanstack-start/docs/library/tanstack-router/search-params.md +10 -10
- package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +8 -8
- package/templates/tanstack-start/docs/library/tanstack-start/index.md +15 -15
- package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +9 -9
- package/templates/tanstack-start/docs/library/tanstack-start/routing.md +6 -6
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +18 -18
- package/templates/tanstack-start/docs/library/tanstack-start/setup.md +4 -4
- package/templates/tanstack-start/docs/library/zod/complex-types.md +11 -11
- package/templates/tanstack-start/docs/library/zod/index.md +8 -8
- package/templates/tanstack-start/docs/library/zod/transforms.md +11 -11
- package/templates/tanstack-start/docs/library/zod/validation.md +9 -9
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
# Better Auth
|
|
2
|
+
|
|
3
|
+
> TypeScript Authentication Library
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<quick_start>
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install better-auth
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Minimal Setup
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
// src/lib/auth.ts - Server
|
|
19
|
+
import { betterAuth } from "better-auth"
|
|
20
|
+
import { prismaAdapter } from "better-auth/adapters/prisma"
|
|
21
|
+
import { prisma } from "@/database/prisma"
|
|
22
|
+
|
|
23
|
+
export const auth = betterAuth({
|
|
24
|
+
database: prismaAdapter(prisma, { provider: "postgresql" }),
|
|
25
|
+
emailAndPassword: { enabled: true },
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
// src/lib/auth-client.ts - Client
|
|
29
|
+
import { createAuthClient } from "better-auth/react"
|
|
30
|
+
|
|
31
|
+
export const authClient = createAuthClient({
|
|
32
|
+
baseURL: process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000",
|
|
33
|
+
})
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Next.js Route Handler
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// app/api/auth/[...all]/route.ts
|
|
40
|
+
import { auth } from "@/lib/auth"
|
|
41
|
+
|
|
42
|
+
export const GET = (request: Request) => auth.handler(request)
|
|
43
|
+
export const POST = (request: Request) => auth.handler(request)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
</quick_start>
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
<setup>
|
|
51
|
+
|
|
52
|
+
## Database Adapters
|
|
53
|
+
|
|
54
|
+
| Adapter | Import | Provider |
|
|
55
|
+
|---------|--------|----------|
|
|
56
|
+
| Prisma | `better-auth/adapters/prisma` | `postgresql`, `mysql`, `sqlite` |
|
|
57
|
+
| Drizzle | `better-auth/adapters/drizzle` | `pg`, `mysql2`, `better-sqlite3` |
|
|
58
|
+
| Kysely | `better-auth/adapters/kysely` | dialect-based |
|
|
59
|
+
|
|
60
|
+
## Auth Config
|
|
61
|
+
|
|
62
|
+
| Option | Type | Default | Description |
|
|
63
|
+
|--------|------|---------|-------------|
|
|
64
|
+
| `database` | `Adapter` | required | Database adapter |
|
|
65
|
+
| `baseURL` | `string` | `http://localhost:3000` | App URL |
|
|
66
|
+
| `basePath` | `string` | `/api/auth` | Auth path |
|
|
67
|
+
| `secret` | `string` | environment variable | JWT secret |
|
|
68
|
+
| `trustedOrigins` | `string[]` | `[]` | CORS allowed origins |
|
|
69
|
+
|
|
70
|
+
## Social Providers
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
export const auth = betterAuth({
|
|
74
|
+
database: prismaAdapter(prisma),
|
|
75
|
+
socialProviders: {
|
|
76
|
+
google: {
|
|
77
|
+
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
78
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
79
|
+
},
|
|
80
|
+
github: {
|
|
81
|
+
clientId: process.env.GITHUB_CLIENT_ID!,
|
|
82
|
+
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Email & Password
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
export const auth = betterAuth({
|
|
92
|
+
emailAndPassword: {
|
|
93
|
+
enabled: true,
|
|
94
|
+
requireEmailVerification: false,
|
|
95
|
+
sendResetPassword: async ({ user, url }) => {
|
|
96
|
+
await sendEmail({
|
|
97
|
+
to: user.email,
|
|
98
|
+
subject: "Reset Password",
|
|
99
|
+
html: `<a href="${url}">Reset</a>`,
|
|
100
|
+
})
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
</setup>
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
<session>
|
|
111
|
+
|
|
112
|
+
## Session Config
|
|
113
|
+
|
|
114
|
+
| Option | Type | Default | Description |
|
|
115
|
+
|--------|------|---------|-------------|
|
|
116
|
+
| `expiresIn` | `number` | `604800` (7 days) | Session expiration time (seconds) |
|
|
117
|
+
| `updateAge` | `number` | `86400` (1 day) | Session update interval (seconds) |
|
|
118
|
+
| `cookieCache.enabled` | `boolean` | `true` | Enable cookie cache |
|
|
119
|
+
| `cookieCache.maxAge` | `number` | `300` (5 min) | Cache validity (seconds) |
|
|
120
|
+
| `cookieCache.strategy` | `'compact' \| 'jwt' \| 'jwe'` | `'compact'` | Cache strategy |
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
export const auth = betterAuth({
|
|
124
|
+
session: {
|
|
125
|
+
expiresIn: 604800, // 7 days
|
|
126
|
+
updateAge: 86400, // Update every 1 day
|
|
127
|
+
cookieCache: {
|
|
128
|
+
enabled: true,
|
|
129
|
+
maxAge: 300, // 5 minutes
|
|
130
|
+
strategy: "compact",
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
})
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Session Methods
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// ✅ Client
|
|
140
|
+
const session = await authClient.getSession()
|
|
141
|
+
const session = await authClient.getSession({ query: { disableCookieCache: true } })
|
|
142
|
+
|
|
143
|
+
// ✅ Server (Server Component)
|
|
144
|
+
import { auth } from "@/lib/auth"
|
|
145
|
+
import { headers } from "next/headers"
|
|
146
|
+
|
|
147
|
+
export default async function Page() {
|
|
148
|
+
const session = await auth.api.getSession({ headers: headers() })
|
|
149
|
+
if (!session?.user) redirect("/login")
|
|
150
|
+
|
|
151
|
+
return <div>Welcome {session.user.name}</div>
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ✅ Server (Server Action)
|
|
155
|
+
"use server"
|
|
156
|
+
|
|
157
|
+
import { auth } from "@/lib/auth"
|
|
158
|
+
import { headers } from "next/headers"
|
|
159
|
+
|
|
160
|
+
export async function getSession() {
|
|
161
|
+
const session = await auth.api.getSession({ headers: headers() })
|
|
162
|
+
return session
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ✅ Update session
|
|
166
|
+
await authClient.updateUser({ name: "New Name" })
|
|
167
|
+
|
|
168
|
+
// ✅ Logout
|
|
169
|
+
await authClient.signOut()
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Custom Session Fields
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { customSession } from "better-auth/plugins"
|
|
176
|
+
|
|
177
|
+
export const auth = betterAuth({
|
|
178
|
+
plugins: [
|
|
179
|
+
customSession({
|
|
180
|
+
schema: {
|
|
181
|
+
session: {
|
|
182
|
+
fields: {
|
|
183
|
+
ipAddress: { type: "string" },
|
|
184
|
+
userAgent: { type: "string" },
|
|
185
|
+
metadata: { type: "json" },
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
async onSessionCreate(session, context) {
|
|
190
|
+
return {
|
|
191
|
+
...session,
|
|
192
|
+
ipAddress: context.request.headers.get("x-forwarded-for") || "unknown",
|
|
193
|
+
userAgent: context.request.headers.get("user-agent") || "unknown",
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
}),
|
|
197
|
+
],
|
|
198
|
+
})
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
</session>
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
<auth_methods>
|
|
206
|
+
|
|
207
|
+
## Email/Password
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
// ✅ Sign up
|
|
211
|
+
await authClient.signUp.email({
|
|
212
|
+
email: "user@example.com",
|
|
213
|
+
password: "password123",
|
|
214
|
+
name: "User Name",
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
// ✅ Sign in
|
|
218
|
+
await authClient.signIn.email({
|
|
219
|
+
email: "user@example.com",
|
|
220
|
+
password: "password123",
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
// ✅ Request password reset
|
|
224
|
+
await authClient.forgetPassword({ email: "user@example.com" })
|
|
225
|
+
|
|
226
|
+
// ✅ Reset password
|
|
227
|
+
await authClient.resetPassword({ token, password: "newpassword" })
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Social Login
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
// ✅ Social sign in
|
|
234
|
+
await authClient.signIn.social({ provider: "google", callbackURL: "/dashboard" })
|
|
235
|
+
await authClient.signIn.social({ provider: "github", callbackURL: "/dashboard" })
|
|
236
|
+
|
|
237
|
+
// ✅ SSO
|
|
238
|
+
await authClient.signIn.sso({ providerId: "provider-id", callbackURL: "/dashboard" })
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Two-Factor Authentication
|
|
242
|
+
|
|
243
|
+
### Server Setup
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import { twoFactor } from "better-auth/plugins"
|
|
247
|
+
|
|
248
|
+
export const auth = betterAuth({
|
|
249
|
+
plugins: [
|
|
250
|
+
twoFactor({
|
|
251
|
+
issuer: "My App",
|
|
252
|
+
otpOptions: {
|
|
253
|
+
async sendOTP({ user, otp }) {
|
|
254
|
+
await sendEmail({
|
|
255
|
+
to: user.email,
|
|
256
|
+
subject: "Your OTP Code",
|
|
257
|
+
html: `Your code: ${otp}`,
|
|
258
|
+
})
|
|
259
|
+
},
|
|
260
|
+
period: 300, // 5 minutes
|
|
261
|
+
length: 6, // 6 digits
|
|
262
|
+
},
|
|
263
|
+
backupCodeLength: 10,
|
|
264
|
+
backupCodeCount: 10,
|
|
265
|
+
}),
|
|
266
|
+
],
|
|
267
|
+
})
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Client Setup
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
import { twoFactorClient } from "better-auth/client/plugins"
|
|
274
|
+
|
|
275
|
+
export const authClient = createAuthClient({
|
|
276
|
+
plugins: [
|
|
277
|
+
twoFactorClient({
|
|
278
|
+
twoFactorPage: "/two-factor",
|
|
279
|
+
}),
|
|
280
|
+
],
|
|
281
|
+
})
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### 2FA Usage
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
// ✅ Enable TOTP
|
|
288
|
+
const { data } = await authClient.twoFactor.enable({ password: "current-password" })
|
|
289
|
+
// data: { totpURI: 'otpauth://...', backupCodes: ['ABCD-1234', ...] }
|
|
290
|
+
|
|
291
|
+
// ✅ Verify TOTP
|
|
292
|
+
await authClient.twoFactor.verifyTotp({ code: "123456" })
|
|
293
|
+
|
|
294
|
+
// ✅ Send OTP
|
|
295
|
+
await authClient.twoFactor.sendOtp()
|
|
296
|
+
|
|
297
|
+
// ✅ Verify OTP
|
|
298
|
+
await authClient.twoFactor.verifyOtp({ code: "123456" })
|
|
299
|
+
|
|
300
|
+
// ✅ Use backup code
|
|
301
|
+
await authClient.twoFactor.useBackupCode({ code: "ABCD-1234" })
|
|
302
|
+
|
|
303
|
+
// ✅ Regenerate backup codes
|
|
304
|
+
const { data } = await authClient.twoFactor.regenerateBackupCodes({ password: "current-password" })
|
|
305
|
+
|
|
306
|
+
// ✅ Disable 2FA
|
|
307
|
+
await authClient.twoFactor.disable({ password: "current-password" })
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
</auth_methods>
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
<plugins>
|
|
315
|
+
|
|
316
|
+
## Plugin System
|
|
317
|
+
|
|
318
|
+
| Plugin | Import | Feature |
|
|
319
|
+
|--------|--------|---------|
|
|
320
|
+
| `multiSession` | `better-auth/plugins` | Multi-session management |
|
|
321
|
+
| `customSession` | `better-auth/plugins` | Extend session fields |
|
|
322
|
+
| `twoFactor` | `better-auth/plugins` | Two-factor authentication |
|
|
323
|
+
| `captcha` | `better-auth/plugins` | CAPTCHA validation |
|
|
324
|
+
|
|
325
|
+
## Multi-Session
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
// ✅ Server
|
|
329
|
+
import { multiSession } from "better-auth/plugins"
|
|
330
|
+
|
|
331
|
+
export const auth = betterAuth({
|
|
332
|
+
plugins: [
|
|
333
|
+
multiSession({
|
|
334
|
+
maximumSessions: 5, // Maximum sessions
|
|
335
|
+
}),
|
|
336
|
+
],
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
// ✅ Client
|
|
340
|
+
import { multiSessionClient } from "better-auth/client/plugins"
|
|
341
|
+
|
|
342
|
+
export const authClient = createAuthClient({
|
|
343
|
+
plugins: [multiSessionClient()],
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
// Usage
|
|
347
|
+
const sessions = await authClient.multiSession.listSessions()
|
|
348
|
+
await authClient.multiSession.revokeSession({ sessionId: "session-id" })
|
|
349
|
+
await authClient.multiSession.revokeOtherSessions()
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## CAPTCHA
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
// ✅ Server
|
|
356
|
+
import { captcha } from "better-auth/plugins"
|
|
357
|
+
|
|
358
|
+
export const auth = betterAuth({
|
|
359
|
+
plugins: [
|
|
360
|
+
captcha({
|
|
361
|
+
provider: "recaptcha",
|
|
362
|
+
siteKey: process.env.RECAPTCHA_SITE_KEY!,
|
|
363
|
+
secretKey: process.env.RECAPTCHA_SECRET_KEY!,
|
|
364
|
+
protectEndpoints: ["/sign-up/email", "/sign-in/email"],
|
|
365
|
+
}),
|
|
366
|
+
],
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
// ✅ Client
|
|
370
|
+
import { captchaClient } from "better-auth/client/plugins"
|
|
371
|
+
|
|
372
|
+
export const authClient = createAuthClient({
|
|
373
|
+
plugins: [
|
|
374
|
+
captchaClient({
|
|
375
|
+
siteKey: process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY!,
|
|
376
|
+
}),
|
|
377
|
+
],
|
|
378
|
+
})
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
</plugins>
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
<advanced>
|
|
386
|
+
|
|
387
|
+
## SIWE (Ethereum)
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
// Server: siwe({ domain, uri })
|
|
391
|
+
// Client:
|
|
392
|
+
const { data } = await authClient.siwe.getNonce()
|
|
393
|
+
const message = await authClient.siwe.prepareMessage({ address: "0x...", nonce: data.nonce })
|
|
394
|
+
const signature = await signer.signMessage(message)
|
|
395
|
+
await authClient.siwe.signIn({ message, signature })
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
## Stateless Mode
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
// ✅ Social login only without DB
|
|
402
|
+
export const auth = betterAuth({
|
|
403
|
+
socialProviders: {
|
|
404
|
+
google: {
|
|
405
|
+
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
406
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
})
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## Redis Secondary Storage
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
import { Redis } from "ioredis"
|
|
416
|
+
|
|
417
|
+
const redis = new Redis()
|
|
418
|
+
|
|
419
|
+
export const auth = betterAuth({
|
|
420
|
+
database: prismaAdapter(prisma),
|
|
421
|
+
secondaryStorage: {
|
|
422
|
+
get: async (key: string) => {
|
|
423
|
+
const value = await redis.get(key)
|
|
424
|
+
return value ? JSON.parse(value) : null
|
|
425
|
+
},
|
|
426
|
+
set: async (key: string, value: any, ttl: number) => {
|
|
427
|
+
await redis.set(key, JSON.stringify(value), "EX", ttl)
|
|
428
|
+
},
|
|
429
|
+
delete: async (key: string) => {
|
|
430
|
+
await redis.del(key)
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
})
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
</advanced>
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
<patterns>
|
|
441
|
+
|
|
442
|
+
## Next.js Integration
|
|
443
|
+
|
|
444
|
+
| Pattern | Usage |
|
|
445
|
+
|---------|-------|
|
|
446
|
+
| Route Handler | `app/api/auth/[...all]/route.ts` |
|
|
447
|
+
| Server Component | `auth.api.getSession({ headers })` |
|
|
448
|
+
| Server Action | `auth.api.getSession({ headers })` |
|
|
449
|
+
| Middleware | `auth.api.getSession({ headers })` + redirect |
|
|
450
|
+
|
|
451
|
+
### Server Component Pattern
|
|
452
|
+
|
|
453
|
+
```typescript
|
|
454
|
+
// app/dashboard/page.tsx
|
|
455
|
+
import { auth } from "@/lib/auth"
|
|
456
|
+
import { headers } from "next/headers"
|
|
457
|
+
import { redirect } from "next/navigation"
|
|
458
|
+
|
|
459
|
+
export default async function DashboardPage() {
|
|
460
|
+
const session = await auth.api.getSession({ headers: headers() })
|
|
461
|
+
|
|
462
|
+
if (!session?.user) {
|
|
463
|
+
redirect("/login")
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return <div>Welcome {session.user.name}</div>
|
|
467
|
+
}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### Server Action Pattern
|
|
471
|
+
|
|
472
|
+
```typescript
|
|
473
|
+
// actions/auth.ts
|
|
474
|
+
"use server"
|
|
475
|
+
|
|
476
|
+
import { auth } from "@/lib/auth"
|
|
477
|
+
import { headers } from "next/headers"
|
|
478
|
+
import { redirect } from "next/navigation"
|
|
479
|
+
|
|
480
|
+
export async function requireAuth() {
|
|
481
|
+
const session = await auth.api.getSession({ headers: headers() })
|
|
482
|
+
|
|
483
|
+
if (!session?.user) {
|
|
484
|
+
redirect("/login")
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return session.user
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
export async function getProtectedData() {
|
|
491
|
+
const user = await requireAuth()
|
|
492
|
+
return prisma.data.findMany({ where: { userId: user.id } })
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
### Middleware Pattern
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
// middleware.ts
|
|
500
|
+
import { auth } from "@/lib/auth"
|
|
501
|
+
import { NextResponse } from "next/server"
|
|
502
|
+
import type { NextRequest } from "next/server"
|
|
503
|
+
|
|
504
|
+
export async function middleware(request: NextRequest) {
|
|
505
|
+
const session = await auth.api.getSession({ headers: request.headers })
|
|
506
|
+
|
|
507
|
+
if (!session?.user) {
|
|
508
|
+
return NextResponse.redirect(new URL("/login", request.url))
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return NextResponse.next()
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
export const config = {
|
|
515
|
+
matcher: ["/dashboard/:path*", "/profile/:path*"],
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
## Common Patterns
|
|
520
|
+
|
|
521
|
+
| Task | Pattern |
|
|
522
|
+
|------|---------|
|
|
523
|
+
| Sign in | `authClient.signIn.email({ email, password })` |
|
|
524
|
+
| Sign up | `authClient.signUp.email({ email, password, name })` |
|
|
525
|
+
| Sign out | `authClient.signOut()` |
|
|
526
|
+
| Get session | `authClient.getSession()` (client) |
|
|
527
|
+
| Get session | `auth.api.getSession({ headers })` (server) |
|
|
528
|
+
| Update user | `authClient.updateUser({ name })` |
|
|
529
|
+
| Reset password | `authClient.forgetPassword({ email })` → `authClient.resetPassword({ token, password })` |
|
|
530
|
+
|
|
531
|
+
## Do's & Don'ts
|
|
532
|
+
|
|
533
|
+
| ✅ Do | ❌ Don't |
|
|
534
|
+
|-------|----------|
|
|
535
|
+
| Use `auth.api.getSession()` on server | Manipulate session directly on client |
|
|
536
|
+
| Use `authClient` on client | Hardcode secrets |
|
|
537
|
+
| Manage secrets with environment variables | Store session tokens in localStorage |
|
|
538
|
+
| HTTPS required in production | Deploy with HTTP in production |
|
|
539
|
+
| Configure `baseURL` per environment | Hardcode absolute paths |
|
|
540
|
+
|
|
541
|
+
</patterns>
|