@prmichaelsen/acp-visualizer 0.1.0

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 (180) hide show
  1. package/README.md +68 -0
  2. package/agent/commands/acp.clarification-address.md +417 -0
  3. package/agent/commands/acp.clarification-capture.md +386 -0
  4. package/agent/commands/acp.clarification-create.md +437 -0
  5. package/agent/commands/acp.clarifications-research.md +326 -0
  6. package/agent/commands/acp.command-create.md +432 -0
  7. package/agent/commands/acp.design-create.md +286 -0
  8. package/agent/commands/acp.design-reference.md +355 -0
  9. package/agent/commands/acp.handoff.md +270 -0
  10. package/agent/commands/acp.index.md +423 -0
  11. package/agent/commands/acp.init.md +546 -0
  12. package/agent/commands/acp.package-create.md +895 -0
  13. package/agent/commands/acp.package-info.md +212 -0
  14. package/agent/commands/acp.package-install.md +539 -0
  15. package/agent/commands/acp.package-list.md +280 -0
  16. package/agent/commands/acp.package-publish.md +541 -0
  17. package/agent/commands/acp.package-remove.md +293 -0
  18. package/agent/commands/acp.package-search.md +307 -0
  19. package/agent/commands/acp.package-update.md +361 -0
  20. package/agent/commands/acp.package-validate.md +540 -0
  21. package/agent/commands/acp.pattern-create.md +386 -0
  22. package/agent/commands/acp.plan.md +587 -0
  23. package/agent/commands/acp.proceed.md +882 -0
  24. package/agent/commands/acp.project-create.md +675 -0
  25. package/agent/commands/acp.project-info.md +312 -0
  26. package/agent/commands/acp.project-list.md +226 -0
  27. package/agent/commands/acp.project-remove.md +379 -0
  28. package/agent/commands/acp.project-set.md +227 -0
  29. package/agent/commands/acp.project-update.md +307 -0
  30. package/agent/commands/acp.projects-restore.md +228 -0
  31. package/agent/commands/acp.projects-sync.md +347 -0
  32. package/agent/commands/acp.report.md +407 -0
  33. package/agent/commands/acp.resume.md +239 -0
  34. package/agent/commands/acp.sessions.md +301 -0
  35. package/agent/commands/acp.status.md +293 -0
  36. package/agent/commands/acp.sync.md +364 -0
  37. package/agent/commands/acp.task-create.md +500 -0
  38. package/agent/commands/acp.update.md +302 -0
  39. package/agent/commands/acp.validate.md +466 -0
  40. package/agent/commands/acp.version-check-for-updates.md +276 -0
  41. package/agent/commands/acp.version-check.md +191 -0
  42. package/agent/commands/acp.version-update.md +289 -0
  43. package/agent/commands/command.template.md +339 -0
  44. package/agent/commands/git.commit.md +526 -0
  45. package/agent/commands/git.init.md +514 -0
  46. package/agent/commands/tanstack-cloudflare.deploy.md +272 -0
  47. package/agent/commands/tanstack-cloudflare.tail.md +275 -0
  48. package/agent/design/.gitkeep +0 -0
  49. package/agent/design/design.template.md +154 -0
  50. package/agent/design/local.dashboard-layout-routing.md +288 -0
  51. package/agent/design/local.data-model-yaml-parsing.md +310 -0
  52. package/agent/design/local.search-filtering.md +331 -0
  53. package/agent/design/local.server-api-auto-refresh.md +235 -0
  54. package/agent/design/local.table-tree-views.md +299 -0
  55. package/agent/design/local.visualizer-requirements.md +349 -0
  56. package/agent/design/requirements.template.md +387 -0
  57. package/agent/index/.gitkeep +0 -0
  58. package/agent/index/acp.core.yaml +137 -0
  59. package/agent/index/local.main.template.yaml +37 -0
  60. package/agent/manifest.template.yaml +13 -0
  61. package/agent/manifest.yaml +302 -0
  62. package/agent/milestones/.gitkeep +0 -0
  63. package/agent/milestones/milestone-1-project-scaffold-data-pipeline.md +67 -0
  64. package/agent/milestones/milestone-1-{title}.template.md +206 -0
  65. package/agent/milestones/milestone-2-dashboard-views-interaction.md +79 -0
  66. package/agent/package.template.yaml +86 -0
  67. package/agent/patterns/.gitkeep +0 -0
  68. package/agent/patterns/bootstrap.template.md +1237 -0
  69. package/agent/patterns/pattern.template.md +382 -0
  70. package/agent/patterns/tanstack-cloudflare.acl-permissions.md +332 -0
  71. package/agent/patterns/tanstack-cloudflare.action-bar-item.md +416 -0
  72. package/agent/patterns/tanstack-cloudflare.api-route-handlers.md +401 -0
  73. package/agent/patterns/tanstack-cloudflare.auth-session-management.md +387 -0
  74. package/agent/patterns/tanstack-cloudflare.card-and-list.md +271 -0
  75. package/agent/patterns/tanstack-cloudflare.chat-engine.md +353 -0
  76. package/agent/patterns/tanstack-cloudflare.confirmation-tokens.md +346 -0
  77. package/agent/patterns/tanstack-cloudflare.durable-objects-websocket.md +516 -0
  78. package/agent/patterns/tanstack-cloudflare.email-service.md +431 -0
  79. package/agent/patterns/tanstack-cloudflare.expander.md +98 -0
  80. package/agent/patterns/tanstack-cloudflare.fcm-push.md +115 -0
  81. package/agent/patterns/tanstack-cloudflare.firebase-anonymous-sessions.md +441 -0
  82. package/agent/patterns/tanstack-cloudflare.firebase-auth.md +348 -0
  83. package/agent/patterns/tanstack-cloudflare.firebase-firestore.md +550 -0
  84. package/agent/patterns/tanstack-cloudflare.firebase-storage.md +369 -0
  85. package/agent/patterns/tanstack-cloudflare.form-controls.md +145 -0
  86. package/agent/patterns/tanstack-cloudflare.global-search-context.md +93 -0
  87. package/agent/patterns/tanstack-cloudflare.image-carousel.md +126 -0
  88. package/agent/patterns/tanstack-cloudflare.library-services.md +553 -0
  89. package/agent/patterns/tanstack-cloudflare.lightbox.md +169 -0
  90. package/agent/patterns/tanstack-cloudflare.markdown-content.md +115 -0
  91. package/agent/patterns/tanstack-cloudflare.mention-suggestions.md +98 -0
  92. package/agent/patterns/tanstack-cloudflare.modal.md +156 -0
  93. package/agent/patterns/tanstack-cloudflare.nextjs-to-tanstack-routing.md +461 -0
  94. package/agent/patterns/tanstack-cloudflare.notifications-engine.md +151 -0
  95. package/agent/patterns/tanstack-cloudflare.oauth-token-refresh.md +90 -0
  96. package/agent/patterns/tanstack-cloudflare.og-metadata.md +296 -0
  97. package/agent/patterns/tanstack-cloudflare.pagination.md +442 -0
  98. package/agent/patterns/tanstack-cloudflare.pill-input.md +220 -0
  99. package/agent/patterns/tanstack-cloudflare.provider-adapter.md +401 -0
  100. package/agent/patterns/tanstack-cloudflare.rate-limiting.md +323 -0
  101. package/agent/patterns/tanstack-cloudflare.scheduled-tasks.md +338 -0
  102. package/agent/patterns/tanstack-cloudflare.searchable-settings.md +375 -0
  103. package/agent/patterns/tanstack-cloudflare.slide-over.md +129 -0
  104. package/agent/patterns/tanstack-cloudflare.ssr-preload.md +571 -0
  105. package/agent/patterns/tanstack-cloudflare.third-party-api-integration.md +508 -0
  106. package/agent/patterns/tanstack-cloudflare.toast-system.md +142 -0
  107. package/agent/patterns/tanstack-cloudflare.unified-header.md +280 -0
  108. package/agent/patterns/tanstack-cloudflare.user-scoped-collections.md +628 -0
  109. package/agent/patterns/tanstack-cloudflare.websocket-manager.md +237 -0
  110. package/agent/patterns/tanstack-cloudflare.wrangler-configuration.md +358 -0
  111. package/agent/patterns/tanstack-cloudflare.zod-schema-validation.md +336 -0
  112. package/agent/progress.template.yaml +161 -0
  113. package/agent/progress.yaml +145 -0
  114. package/agent/schemas/package.schema.yaml +276 -0
  115. package/agent/scripts/acp.common.sh +1781 -0
  116. package/agent/scripts/acp.install.sh +333 -0
  117. package/agent/scripts/acp.package-create.sh +924 -0
  118. package/agent/scripts/acp.package-info.sh +288 -0
  119. package/agent/scripts/acp.package-install.sh +893 -0
  120. package/agent/scripts/acp.package-list.sh +311 -0
  121. package/agent/scripts/acp.package-publish.sh +420 -0
  122. package/agent/scripts/acp.package-remove.sh +348 -0
  123. package/agent/scripts/acp.package-search.sh +156 -0
  124. package/agent/scripts/acp.package-update.sh +517 -0
  125. package/agent/scripts/acp.package-validate.sh +1018 -0
  126. package/agent/scripts/acp.uninstall.sh +85 -0
  127. package/agent/scripts/acp.version-check-for-updates.sh +98 -0
  128. package/agent/scripts/acp.version-check.sh +47 -0
  129. package/agent/scripts/acp.version-update.sh +176 -0
  130. package/agent/scripts/acp.yaml-parser.sh +985 -0
  131. package/agent/scripts/acp.yaml-validate.sh +205 -0
  132. package/agent/tasks/.gitkeep +0 -0
  133. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-1-initialize-tanstack-start-project.md +210 -0
  134. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-2-implement-data-model-yaml-parser.md +294 -0
  135. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-3-build-server-api-data-loading.md +193 -0
  136. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-4-add-auto-refresh-sse.md +262 -0
  137. package/agent/tasks/milestone-2-dashboard-views-interaction/task-10-polish-integration-testing.md +156 -0
  138. package/agent/tasks/milestone-2-dashboard-views-interaction/task-5-build-dashboard-layout-routing.md +178 -0
  139. package/agent/tasks/milestone-2-dashboard-views-interaction/task-6-build-overview-page.md +141 -0
  140. package/agent/tasks/milestone-2-dashboard-views-interaction/task-7-implement-milestone-table-view.md +153 -0
  141. package/agent/tasks/milestone-2-dashboard-views-interaction/task-8-implement-milestone-tree-view.md +174 -0
  142. package/agent/tasks/milestone-2-dashboard-views-interaction/task-9-implement-search-filtering.md +233 -0
  143. package/agent/tasks/task-1-{title}.template.md +244 -0
  144. package/bin/visualize.mjs +84 -0
  145. package/package.json +48 -0
  146. package/src/components/ExtraFieldsBadge.tsx +15 -0
  147. package/src/components/FilterBar.tsx +33 -0
  148. package/src/components/Header.tsx +23 -0
  149. package/src/components/MilestoneTable.tsx +167 -0
  150. package/src/components/MilestoneTree.tsx +84 -0
  151. package/src/components/ProgressBar.tsx +20 -0
  152. package/src/components/SearchInput.tsx +22 -0
  153. package/src/components/Sidebar.tsx +54 -0
  154. package/src/components/StatusBadge.tsx +23 -0
  155. package/src/components/StatusDot.tsx +12 -0
  156. package/src/components/TaskList.tsx +36 -0
  157. package/src/components/ViewToggle.tsx +31 -0
  158. package/src/lib/config.ts +8 -0
  159. package/src/lib/file-watcher.ts +43 -0
  160. package/src/lib/search.ts +48 -0
  161. package/src/lib/types.ts +73 -0
  162. package/src/lib/useAutoRefresh.ts +31 -0
  163. package/src/lib/useCollapse.ts +31 -0
  164. package/src/lib/useFilteredData.ts +55 -0
  165. package/src/lib/yaml-loader-real.spec.ts +47 -0
  166. package/src/lib/yaml-loader.spec.ts +201 -0
  167. package/src/lib/yaml-loader.ts +265 -0
  168. package/src/routeTree.gen.ts +140 -0
  169. package/src/router.tsx +10 -0
  170. package/src/routes/__root.tsx +75 -0
  171. package/src/routes/api/watch.ts +29 -0
  172. package/src/routes/index.tsx +115 -0
  173. package/src/routes/milestones.tsx +50 -0
  174. package/src/routes/search.tsx +84 -0
  175. package/src/routes/tasks.tsx +63 -0
  176. package/src/services/progress-database.service.ts +46 -0
  177. package/src/styles.css +25 -0
  178. package/tsconfig.json +24 -0
  179. package/vite.config.ts +16 -0
  180. package/vitest.config.ts +27 -0
@@ -0,0 +1,348 @@
1
+ # Firebase Authentication
2
+
3
+ **Category**: Code
4
+ **Applicable To**: All server-side auth verification, session management, API route protection, and SSR auth checks
5
+ **Status**: Stable
6
+
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ This pattern covers how Firebase Admin SDK authentication is used throughout the project: session cookie management, token verification, route guards, SSR auth, and the client-to-server auth handshake. The project uses a dual-layer system: Firebase Client SDK for client-side auth (sign-in/sign-up) and Firebase Admin SDK (`@prmichaelsen/firebase-admin-sdk-v8`) for server-side session management via long-lived session cookies.
12
+
13
+ ---
14
+
15
+ ## When to Use This Pattern
16
+
17
+ **Use this pattern when:**
18
+ - Adding a new API route that requires authentication
19
+ - Adding a new SSR `beforeLoad` that needs user context
20
+ - Building a new server function (`createServerFn`) that accesses user data
21
+ - Implementing admin-only routes or features
22
+
23
+ **Don't use this pattern when:**
24
+ - Working on purely client-side components with no server interaction
25
+ - Building public/unauthenticated endpoints (use no auth check)
26
+ - Implementing MCP server auth (use `mcp-jwt.ts` JWT tokens instead)
27
+
28
+ ---
29
+
30
+ ## Core Principles
31
+
32
+ 1. **Session Cookies Over ID Tokens**: Server-side auth uses 14-day session cookies, not short-lived ID tokens
33
+ 2. **Dual Verification Fallback**: `verifySessionCookie()` first, then `verifyIdToken()` for migration compatibility
34
+ 3. **Null on Failure**: Auth functions return `null` on error, never throw — callers decide how to respond
35
+ 4. **Always Initialize First**: Call `initFirebaseAdmin()` before any auth operation
36
+ 5. **Anonymous Users Are Valid**: Anonymous sessions are real auth sessions — check `isAnonymous` when restricting features
37
+
38
+ ---
39
+
40
+ ## Implementation
41
+
42
+ ### Auth Flow Overview
43
+
44
+ ```
45
+ Client Server
46
+ │ │
47
+ ├─ Firebase signIn/signUp ──────► │
48
+ │ (gets ID token) │
49
+ │ │
50
+ ├─ POST /api/auth/login ──────────► │
51
+ │ { idToken, turnstileToken? } │
52
+ │ ├─ verifyIdToken(idToken)
53
+ │ ├─ createSessionCookie(idToken, 14d)
54
+ │ ├─ Set-Cookie: session=...
55
+ │ ◄──────────────────────────────── │
56
+ │ │
57
+ ├─ GET /api/some-endpoint ─────────► │
58
+ │ Cookie: session=... ├─ getServerSession(request)
59
+ │ │ └─ verifySessionCookie(cookie)
60
+ │ │ └─ fallback: verifyIdToken(cookie)
61
+ │ ◄─── { data } ────────────────── │
62
+ ```
63
+
64
+ ### Key Functions
65
+
66
+ #### `initFirebaseAdmin()` — SDK Initialization
67
+
68
+ **File**: `src/lib/firebase-admin.ts`
69
+
70
+ ```typescript
71
+ import { initializeApp as _initializeApp } from '@prmichaelsen/firebase-admin-sdk-v8'
72
+
73
+ export function initFirebaseAdmin() {
74
+ _initializeApp({
75
+ serviceAccount: process.env.FIREBASE_ADMIN_SERVICE_ACCOUNT_KEY,
76
+ projectId: process.env.FIREBASE_PROJECT_ID,
77
+ })
78
+ }
79
+ ```
80
+
81
+ Called at the start of every API route handler and SSR `beforeLoad`. Idempotent — safe to call multiple times.
82
+
83
+ #### `getServerSession(request)` — Session Verification
84
+
85
+ **File**: `src/lib/auth/session.ts`
86
+
87
+ ```typescript
88
+ import { verifyIdToken, verifySessionCookie } from '@prmichaelsen/firebase-admin-sdk-v8'
89
+
90
+ export async function getServerSession(request: Request): Promise<ServerSession | null> {
91
+ const sessionCookie = getSessionCookie(request)
92
+ if (!sessionCookie) return null
93
+
94
+ let decodedToken
95
+ try {
96
+ decodedToken = await verifySessionCookie(sessionCookie)
97
+ } catch {
98
+ // Migration fallback — old ID tokens in cookies
99
+ decodedToken = await verifyIdToken(sessionCookie)
100
+ }
101
+
102
+ const isAnonymous = decodedToken.firebase?.sign_in_provider === 'anonymous' || !decodedToken.email
103
+
104
+ return {
105
+ user: {
106
+ uid: decodedToken.sub,
107
+ email: decodedToken.email || null,
108
+ displayName: decodedToken.name || null,
109
+ photoURL: decodedToken.picture || null,
110
+ emailVerified: decodedToken.email_verified || false,
111
+ isAnonymous,
112
+ }
113
+ }
114
+ }
115
+ ```
116
+
117
+ #### `getAuthSession()` — TanStack Server Function
118
+
119
+ **File**: `src/lib/auth/server-fn.ts`
120
+
121
+ ```typescript
122
+ export const getAuthSession = createServerFn({ method: 'GET' }).handler(async () => {
123
+ initFirebaseAdmin()
124
+ const session = await getServerSession(getRequest())
125
+ return session?.user || null
126
+ })
127
+ ```
128
+
129
+ Uses `getRequest()` from `@tanstack/react-start/server` to access the Request object.
130
+
131
+ #### `createSessionCookie(idToken)` — Cookie Creation
132
+
133
+ **File**: `src/lib/auth/session.ts`
134
+
135
+ ```typescript
136
+ export async function createSessionCookie(idToken: string): Promise<string> {
137
+ const sessionCookie = await createFirebaseSessionCookie(idToken, {
138
+ expiresIn: 60 * 60 * 24 * 14 * 1000 // 14 days
139
+ })
140
+ return sessionCookie
141
+ }
142
+ ```
143
+
144
+ #### Route Guards
145
+
146
+ **File**: `src/lib/auth/guards.ts`
147
+
148
+ ```typescript
149
+ export async function requireAuth(request: Request): Promise<Response | null> {
150
+ const session = await getServerSession(request)
151
+ if (!session?.user) {
152
+ return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401 })
153
+ }
154
+ return null // null = authorized
155
+ }
156
+
157
+ export async function requireAdmin(request: Request): Promise<Response | null> {
158
+ const session = await getServerSession(request)
159
+ if (!session?.user) return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401 })
160
+
161
+ const ownerEmails = (process.env.OWNER_EMAILS || '').split(',').map(e => e.trim())
162
+ if (!session.user.email || !ownerEmails.includes(session.user.email)) {
163
+ return new Response(JSON.stringify({ error: 'Forbidden' }), { status: 403 })
164
+ }
165
+ return null
166
+ }
167
+ ```
168
+
169
+ ---
170
+
171
+ ## Examples
172
+
173
+ ### Example 1: API Route with Auth
174
+
175
+ ```typescript
176
+ // src/routes/api/some-endpoint.tsx
177
+ GET: async () => {
178
+ initFirebaseAdmin()
179
+
180
+ const user = await getAuthSession()
181
+ if (!user || user.isAnonymous) {
182
+ return new Response(JSON.stringify({ error: 'Unauthorized' }), {
183
+ status: 401,
184
+ headers: { 'Content-Type': 'application/json' },
185
+ })
186
+ }
187
+
188
+ const data = await SomeDatabaseService.getData(user.uid)
189
+ return new Response(JSON.stringify({ data }), {
190
+ status: 200,
191
+ headers: { 'Content-Type': 'application/json' },
192
+ })
193
+ }
194
+ ```
195
+
196
+ ### Example 2: SSR beforeLoad with Auth Redirect
197
+
198
+ ```typescript
199
+ // src/routes/settings/index.tsx
200
+ export const Route = createFileRoute('/settings/')({
201
+ beforeLoad: (async ({ context }: any) => {
202
+ const user = context.initialUser // From root beforeLoad
203
+ if (!user || user.isAnonymous) {
204
+ throw redirect({
205
+ to: '/auth',
206
+ search: { redirect_url: '/settings' },
207
+ })
208
+ }
209
+ return { initialUser: user }
210
+ }) as any,
211
+ component: SettingsPage,
212
+ })
213
+ ```
214
+
215
+ ### Example 3: Server Function with Auth
216
+
217
+ ```typescript
218
+ const updateProfile = createServerFn({ method: 'POST' })
219
+ .inputValidator((data: UpdateProfileInput) => data)
220
+ .handler(async ({ data }) => {
221
+ initFirebaseAdmin()
222
+ const session = await getServerSession(getRequest())
223
+ if (!session?.user) throw new Error('Unauthorized')
224
+
225
+ return await ProfileDatabaseService.updateProfile(session.user.uid, data)
226
+ })
227
+ ```
228
+
229
+ ### Example 4: Root Route — Global Auth Preloading
230
+
231
+ ```typescript
232
+ // src/routes/__root.tsx
233
+ beforeLoad: async () => {
234
+ const user = await getAuthSession()
235
+
236
+ let initialAIConsent, initialTosAccepted
237
+ if (typeof window === 'undefined' && user && !user.isAnonymous) {
238
+ initFirebaseAdmin()
239
+ const [consent, tos] = await Promise.all([
240
+ ConsentDatabaseService.getAIConsent(user.uid),
241
+ TosConsentDatabaseService.hasAcceptedCurrentTos(user.uid),
242
+ ])
243
+ initialAIConsent = consent?.ai_data_sharing ?? null
244
+ initialTosAccepted = tos
245
+ }
246
+
247
+ return { initialUser: user, initialAIConsent, initialTosAccepted }
248
+ }
249
+ ```
250
+
251
+ ---
252
+
253
+ ## Anti-Patterns
254
+
255
+ ### Using `getAuthSession()` Where You Have `context.initialUser`
256
+
257
+ ```typescript
258
+ // Bad: Redundant server function call when root already provides user
259
+ beforeLoad: async () => {
260
+ const user = await getAuthSession() // Unnecessary extra call
261
+ if (!user) throw redirect({ to: '/auth' })
262
+ }
263
+
264
+ // Good: Use context from root beforeLoad
265
+ beforeLoad: async ({ context }: any) => {
266
+ const user = context.initialUser // Already fetched by root
267
+ if (!user) throw redirect({ to: '/auth' })
268
+ }
269
+ ```
270
+
271
+ ### Throwing on Auth Failure in Session Functions
272
+
273
+ ```typescript
274
+ // Bad: Throws — callers can't distinguish auth failure from server error
275
+ export async function getServerSession(request: Request) {
276
+ const cookie = getSessionCookie(request)
277
+ if (!cookie) throw new Error('No session') // Don't throw
278
+ }
279
+
280
+ // Good: Returns null — caller decides the response
281
+ export async function getServerSession(request: Request): Promise<ServerSession | null> {
282
+ const cookie = getSessionCookie(request)
283
+ if (!cookie) return null
284
+ }
285
+ ```
286
+
287
+ ### Forgetting `initFirebaseAdmin()` in API Routes
288
+
289
+ ```typescript
290
+ // Bad: Will fail on first request
291
+ GET: async () => {
292
+ const user = await getAuthSession() // Firebase not initialized!
293
+ }
294
+
295
+ // Good: Always initialize
296
+ GET: async () => {
297
+ initFirebaseAdmin()
298
+ const user = await getAuthSession()
299
+ }
300
+ ```
301
+
302
+ ---
303
+
304
+ ## Key Design Decisions
305
+
306
+ ### Session Management
307
+
308
+ | Decision | Choice | Rationale |
309
+ |---|---|---|
310
+ | Session mechanism | 14-day session cookie | Longer-lived than ID tokens (1 hour), reduces re-auth |
311
+ | Cookie flags | HttpOnly, SameSite=Lax, Secure (prod) | Prevents XSS token theft; Secure disabled on localhost |
312
+ | Token fallback | verifySessionCookie → verifyIdToken | Migration compatibility for old ID token cookies |
313
+ | Anonymous users | Auto-created on first visit | Enables chat in The Void without signup |
314
+
315
+ ### Auth Architecture
316
+
317
+ | Decision | Choice | Rationale |
318
+ |---|---|---|
319
+ | Admin detection | Email match against OWNER_EMAILS env | Simple, no separate admin role system needed |
320
+ | MCP auth | Separate JWT system (mcp-jwt.ts) | MCP servers need stateless tokens, not session cookies |
321
+ | Rate limiting | 5/min login, 3/5min signup | Prevent brute force and spam signups |
322
+ | CAPTCHA | Turnstile for signups, fail-open | Block bots but don't break auth if Turnstile API is down |
323
+
324
+ ---
325
+
326
+ ## Checklist for Implementation
327
+
328
+ - [ ] Call `initFirebaseAdmin()` before any auth operation
329
+ - [ ] Use `getAuthSession()` for server functions, `getServerSession(request)` for API routes
330
+ - [ ] Check `isAnonymous` when the feature requires a real account
331
+ - [ ] Return `null` on auth failure in utility functions (don't throw)
332
+ - [ ] Return 401 for unauthenticated, 403 for forbidden in API routes
333
+ - [ ] Use `context.initialUser` in `beforeLoad` instead of re-calling `getAuthSession()`
334
+ - [ ] Redirect to `/auth?redirect_url=...` for protected pages, not just `/auth`
335
+
336
+ ---
337
+
338
+ ## Related Patterns
339
+
340
+ - **[Database Service Conventions](./database-service-conventions.md)**: Auth-verified userId flows into all database service calls
341
+ - **[SSR Preload](./ssr-preload.md)**: SSR `beforeLoad` uses auth context for server-side data fetching
342
+
343
+ ---
344
+
345
+ **Status**: Stable
346
+ **Recommendation**: Follow this pattern for all new API routes, server functions, and SSR routes requiring authentication
347
+ **Last Updated**: 2026-03-14
348
+ **Contributors**: Community