@kood/claude-code 0.3.6 → 0.3.7

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.
Files changed (34) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +1 -1
  3. package/templates/nextjs/CLAUDE.md +228 -0
  4. package/templates/nextjs/docs/design.md +558 -0
  5. package/templates/nextjs/docs/guides/conventions.md +343 -0
  6. package/templates/nextjs/docs/guides/getting-started.md +367 -0
  7. package/templates/nextjs/docs/guides/routes.md +342 -0
  8. package/templates/nextjs/docs/library/better-auth/index.md +541 -0
  9. package/templates/nextjs/docs/library/nextjs/app-router.md +269 -0
  10. package/templates/nextjs/docs/library/nextjs/caching.md +351 -0
  11. package/templates/nextjs/docs/library/nextjs/index.md +291 -0
  12. package/templates/nextjs/docs/library/nextjs/middleware.md +391 -0
  13. package/templates/nextjs/docs/library/nextjs/route-handlers.md +382 -0
  14. package/templates/nextjs/docs/library/nextjs/server-actions.md +366 -0
  15. package/templates/nextjs/docs/library/prisma/cloudflare-d1.md +76 -0
  16. package/templates/nextjs/docs/library/prisma/config.md +77 -0
  17. package/templates/nextjs/docs/library/prisma/crud.md +90 -0
  18. package/templates/nextjs/docs/library/prisma/index.md +73 -0
  19. package/templates/nextjs/docs/library/prisma/relations.md +69 -0
  20. package/templates/nextjs/docs/library/prisma/schema.md +98 -0
  21. package/templates/nextjs/docs/library/prisma/setup.md +49 -0
  22. package/templates/nextjs/docs/library/prisma/transactions.md +50 -0
  23. package/templates/nextjs/docs/library/tanstack-query/index.md +66 -0
  24. package/templates/nextjs/docs/library/tanstack-query/invalidation.md +54 -0
  25. package/templates/nextjs/docs/library/tanstack-query/optimistic-updates.md +77 -0
  26. package/templates/nextjs/docs/library/tanstack-query/use-mutation.md +63 -0
  27. package/templates/nextjs/docs/library/tanstack-query/use-query.md +70 -0
  28. package/templates/nextjs/docs/library/zod/complex-types.md +61 -0
  29. package/templates/nextjs/docs/library/zod/index.md +56 -0
  30. package/templates/nextjs/docs/library/zod/transforms.md +51 -0
  31. package/templates/nextjs/docs/library/zod/validation.md +70 -0
  32. package/templates/tanstack-start/CLAUDE.md +7 -3
  33. package/templates/tanstack-start/docs/guides/hooks.md +28 -0
  34. package/templates/tanstack-start/docs/guides/routes.md +29 -10
@@ -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 - 서버
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 - 클라이언트
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 기반 |
59
+
60
+ ## Auth Config
61
+
62
+ | 옵션 | 타입 | 기본값 | 설명 |
63
+ |------|------|--------|------|
64
+ | `database` | `Adapter` | 필수 | DB 어댑터 |
65
+ | `baseURL` | `string` | `http://localhost:3000` | 앱 URL |
66
+ | `basePath` | `string` | `/api/auth` | Auth 경로 |
67
+ | `secret` | `string` | 환경변수 | JWT 시크릿 |
68
+ | `trustedOrigins` | `string[]` | `[]` | CORS 허용 오리진 |
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
+ | 옵션 | 타입 | 기본값 | 설명 |
115
+ |------|------|--------|------|
116
+ | `expiresIn` | `number` | `604800` (7일) | 세션 만료 시간 (초) |
117
+ | `updateAge` | `number` | `86400` (1일) | 세션 갱신 주기 (초) |
118
+ | `cookieCache.enabled` | `boolean` | `true` | 쿠키 캐시 활성화 |
119
+ | `cookieCache.maxAge` | `number` | `300` (5분) | 캐시 유효 시간 (초) |
120
+ | `cookieCache.strategy` | `'compact' \| 'jwt' \| 'jwe'` | `'compact'` | 캐시 전략 |
121
+
122
+ ```typescript
123
+ export const auth = betterAuth({
124
+ session: {
125
+ expiresIn: 604800, // 7일
126
+ updateAge: 86400, // 1일마다 갱신
127
+ cookieCache: {
128
+ enabled: true,
129
+ maxAge: 300, // 5분
130
+ strategy: "compact",
131
+ },
132
+ },
133
+ })
134
+ ```
135
+
136
+ ## Session Methods
137
+
138
+ ```typescript
139
+ // ✅ 클라이언트
140
+ const session = await authClient.getSession()
141
+ const session = await authClient.getSession({ query: { disableCookieCache: true } })
142
+
143
+ // ✅ 서버 (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 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
+ // ✅ 세션 업데이트
166
+ await authClient.updateUser({ name: "New Name" })
167
+
168
+ // ✅ 로그아웃
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
+ // ✅ 회원가입
211
+ await authClient.signUp.email({
212
+ email: "user@example.com",
213
+ password: "password123",
214
+ name: "User Name",
215
+ })
216
+
217
+ // ✅ 로그인
218
+ await authClient.signIn.email({
219
+ email: "user@example.com",
220
+ password: "password123",
221
+ })
222
+
223
+ // ✅ 비밀번호 재설정 요청
224
+ await authClient.forgetPassword({ email: "user@example.com" })
225
+
226
+ // ✅ 비밀번호 재설정
227
+ await authClient.resetPassword({ token, password: "newpassword" })
228
+ ```
229
+
230
+ ## Social Login
231
+
232
+ ```typescript
233
+ // ✅ 소셜 로그인
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분
261
+ length: 6, // 6자리
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
+ // ✅ TOTP 활성화
288
+ const { data } = await authClient.twoFactor.enable({ password: "current-password" })
289
+ // data: { totpURI: 'otpauth://...', backupCodes: ['ABCD-1234', ...] }
290
+
291
+ // ✅ TOTP 검증
292
+ await authClient.twoFactor.verifyTotp({ code: "123456" })
293
+
294
+ // ✅ OTP 전송
295
+ await authClient.twoFactor.sendOtp()
296
+
297
+ // ✅ OTP 검증
298
+ await authClient.twoFactor.verifyOtp({ code: "123456" })
299
+
300
+ // ✅ 백업 코드 사용
301
+ await authClient.twoFactor.useBackupCode({ code: "ABCD-1234" })
302
+
303
+ // ✅ 백업 코드 재생성
304
+ const { data } = await authClient.twoFactor.regenerateBackupCodes({ password: "current-password" })
305
+
306
+ // ✅ 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 | 기능 |
319
+ |--------|--------|------|
320
+ | `multiSession` | `better-auth/plugins` | 다중 세션 관리 |
321
+ | `customSession` | `better-auth/plugins` | 세션 필드 확장 |
322
+ | `twoFactor` | `better-auth/plugins` | 2단계 인증 |
323
+ | `captcha` | `better-auth/plugins` | CAPTCHA 검증 |
324
+
325
+ ## Multi-Session
326
+
327
+ ```typescript
328
+ // ✅ 서버
329
+ import { multiSession } from "better-auth/plugins"
330
+
331
+ export const auth = betterAuth({
332
+ plugins: [
333
+ multiSession({
334
+ maximumSessions: 5, // 최대 세션 수
335
+ }),
336
+ ],
337
+ })
338
+
339
+ // ✅ 클라이언트
340
+ import { multiSessionClient } from "better-auth/client/plugins"
341
+
342
+ export const authClient = createAuthClient({
343
+ plugins: [multiSessionClient()],
344
+ })
345
+
346
+ // 사용
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
+ // ✅ 서버
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
+ // ✅ 클라이언트
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
+ // 서버: siwe({ domain, uri })
391
+ // 클라이언트:
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
+ // ✅ 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
+ | 패턴 | 용도 |
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
+ | 작업 | 패턴 |
522
+ |------|------|
523
+ | 로그인 | `authClient.signIn.email({ email, password })` |
524
+ | 회원가입 | `authClient.signUp.email({ email, password, name })` |
525
+ | 로그아웃 | `authClient.signOut()` |
526
+ | 세션 조회 | `authClient.getSession()` (클라이언트) |
527
+ | 세션 조회 | `auth.api.getSession({ headers })` (서버) |
528
+ | 사용자 업데이트 | `authClient.updateUser({ name })` |
529
+ | 비밀번호 재설정 | `authClient.forgetPassword({ email })` → `authClient.resetPassword({ token, password })` |
530
+
531
+ ## Do's & Don'ts
532
+
533
+ | ✅ Do | ❌ Don't |
534
+ |-------|----------|
535
+ | `auth.api.getSession()` 서버에서 사용 | 클라이언트에서 직접 세션 조작 |
536
+ | `authClient` 클라이언트에서 사용 | 하드코딩된 시크릿 |
537
+ | 환경변수로 시크릿 관리 | 세션 토큰 로컬스토리지 저장 |
538
+ | HTTPS 프로덕션 필수 | HTTP 프로덕션 배포 |
539
+ | `baseURL` 환경별 설정 | 절대 경로 하드코딩 |
540
+
541
+ </patterns>