style(13-03): apply Prettier formatting across repo

Mechanical reformat — no logic changes. 398 files changed, 19125
insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc
(singleQuote:true, semi:true, tabWidth:2, trailingComma:all,
printWidth:100). Isolated per D-13-08 for reviewability.
This commit is contained in:
Lucas Berger
2026-06-11 20:35:18 -04:00
parent 4bc0445173
commit 982438dc10
398 changed files with 19050 additions and 16382 deletions
@@ -3,7 +3,7 @@ phase: 01-foundation-broker-spike
plan: 02
type: execute
wave: 2
depends_on: ["01-01"]
depends_on: ['01-01']
files_modified:
- apps/api/src/auth/middleware.ts
- apps/api/src/auth/user.ts
@@ -19,33 +19,33 @@ requirements: [AUTH-01, AUTH-02, AUTH-03]
must_haves:
truths:
- "An unauthenticated request to /api/* is redirected to Authelia's authorize endpoint (302)"
- "After login, the OIDC callback upserts a users row keyed by oidc_iss + oidc_sub (never email)"
- "Each member is auto-assigned a stable, distinct color from a curated palette on first login; re-login returns the same color"
- "Session persists via @hono/oidc-auth refresh-token rotation — no iframe, refresh held backend-side"
- 'After login, the OIDC callback upserts a users row keyed by oidc_iss + oidc_sub (never email)'
- 'Each member is auto-assigned a stable, distinct color from a curated palette on first login; re-login returns the same color'
- 'Session persists via @hono/oidc-auth refresh-token rotation — no iframe, refresh held backend-side'
- "GET /api/me returns the authenticated user's identity + color"
- "The PWA shell renders the logged-in member's name and color swatch"
artifacts:
- path: "apps/api/src/auth/user.ts"
provides: "upsertUser(oidcIss, oidcSub, displayName) with round-robin color assignment"
exports: ["upsertUser", "COLOR_PALETTE"]
- path: "apps/api/src/auth/middleware.ts"
provides: "oidcAuthMiddleware wiring + getAuth → upsertUser bridge"
- path: "apps/api/src/routes/me.ts"
provides: "GET /api/me → { user: { id, displayName, color } }"
exports: ["meRouter"]
- path: 'apps/api/src/auth/user.ts'
provides: 'upsertUser(oidcIss, oidcSub, displayName) with round-robin color assignment'
exports: ['upsertUser', 'COLOR_PALETTE']
- path: 'apps/api/src/auth/middleware.ts'
provides: 'oidcAuthMiddleware wiring + getAuth → upsertUser bridge'
- path: 'apps/api/src/routes/me.ts'
provides: 'GET /api/me → { user: { id, displayName, color } }'
exports: ['meRouter']
key_links:
- from: "apps/api/src/routes/me.ts"
to: "apps/api/src/auth/user.ts"
via: "upsertUser call"
- from: 'apps/api/src/routes/me.ts'
to: 'apps/api/src/auth/user.ts'
via: 'upsertUser call'
pattern: "upsertUser\\("
- from: "apps/api/src/index.ts"
to: "@hono/oidc-auth"
via: "oidcAuthMiddleware on /api/*"
pattern: "oidcAuthMiddleware"
- from: "apps/pwa/src/App.tsx"
to: "/api/me"
via: "React Query fetch"
pattern: "api/me"
- from: 'apps/api/src/index.ts'
to: '@hono/oidc-auth'
via: 'oidcAuthMiddleware on /api/*'
pattern: 'oidcAuthMiddleware'
- from: 'apps/pwa/src/App.tsx'
to: '/api/me'
via: 'React Query fetch'
pattern: 'api/me'
---
<objective>
@@ -73,6 +73,7 @@ Output: Working Authelia OIDC login, stable identity + color, /api/me, authentic
</context>
<artifacts_produced>
## Artifacts this phase produces (Plan 02)
New files: `apps/api/src/auth/middleware.ts`, `apps/api/src/auth/user.ts`, `apps/api/src/routes/me.ts`, `apps/pwa/src/api/client.ts`.
@@ -105,6 +106,7 @@ New env vars: `OIDC_AUTH_SECRET`, `OIDC_ISSUER`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_
Create `src/auth/user.ts` exporting `COLOR_PALETTE` (a curated array of >=4 visually-distinct, accessible hex hues per D-06 / Claude's Discretion — e.g. calm blue, warm coral, forest green, soft purple; exact values Claude's choice) and `upsertUser(oidcIss, oidcSub, displayName?)`. Logic per RESEARCH example: SELECT existing by `and(eq(users.oidcIss, oidcIss), eq(users.oidcSub, oidcSub))`; if present return it; else COUNT existing users, assign `COLOR_PALETTE[count % length]`, INSERT, return the new row. Use `$returningId()` then re-select (mysql2 has no RETURNING). Never key on email.
Fill `tests/auth/user.test.ts` GREEN using the test-DB fixture (tests/helpers/db.ts): assert (a) first insert assigns palette[0]; (b) second user assigns palette[1]; (c) re-upsert of user 1 returns the identical row + color and does not create a duplicate; (d) lookup is by iss+sub.
</action>
<verify>
<automated>cd apps/api && pnpm vitest run tests/auth/user.test.ts --reporter=verbose</automated>
@@ -136,6 +138,7 @@ New env vars: `OIDC_AUTH_SECRET`, `OIDC_ISSUER`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_
PWA: create `apps/pwa/src/api/client.ts` with a typed `fetchMe()` (GET /api/me, credentials: 'include'). Update `App.tsx`: React Query `useQuery(['me'], fetchMe)`; on 401/redirect the browser follows Authelia (full-page). Render the member's displayName and a color swatch using `user.color`. Keep the /health indicator from Plan 01.
Also record the Authelia client registration YAML (from RESEARCH Pattern 1) in the SUMMARY so the operator can paste it into Authelia's configuration.yml — this is the only human-side config (no code change in this repo).
</action>
<verify>
<automated>cd apps/api && pnpm exec tsc --noEmit && grep -q "oidcAuthMiddleware" src/index.ts && grep -q "OIDC_AUTH_EXTERNAL_URL" ../../.env.example && grep -q "upsertUser" src/routes/me.ts</automated>
@@ -155,24 +158,26 @@ New env vars: `OIDC_AUTH_SECRET`, `OIDC_ISSUER`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
|----------|-------------|
| Browser → Pangolin → Hono /api/* | Untrusted client; only authenticated requests cross (OIDC session cookie) |
| Authelia → /callback | OIDC authorization-code exchange; PKCE + state validate the callback |
| Hono → Authelia token endpoint | Backend confidential client; client_secret + refresh token never reach the browser |
| Boundary | Description |
| --------------------------------- | ---------------------------------------------------------------------------------- |
| Browser → Pangolin → Hono /api/\* | Untrusted client; only authenticated requests cross (OIDC session cookie) |
| Authelia → /callback | OIDC authorization-code exchange; PKCE + state validate the callback |
| Hono → Authelia token endpoint | Backend confidential client; client_secret + refresh token never reach the browser |
## STRIDE Threat Register
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-02-01 | Spoofing | OIDC redirect_uri | mitigate | Authelia validates exact match; OIDC_REDIRECT_URI env must equal the registered URI; OIDC_AUTH_EXTERNAL_URL set so Pangolin Host header cannot forge the redirect (Pitfall 1) |
| T-02-02 | Spoofing | CSRF on /callback | mitigate | @hono/oidc-auth uses PKCE (state + code_verifier); require_pkce true, S256 in Authelia client |
| T-02-03 | Tampering | OIDC session JWT cookie | mitigate | Cookie signed with OIDC_AUTH_SECRET (32+ char), httpOnly + Secure + SameSite; verified every request |
| T-02-04 | Information Disclosure | Refresh token / client_secret | mitigate | Backend-only (D-12); never serialized to frontend; not logged; OIDC_CLIENT_SECRET is the plain secret in env, never committed |
| T-02-05 | Elevation of Privilege | /api/* without auth | mitigate | oidcAuthMiddleware mounted on /api/*; no guest access (ASVS V4) |
| T-02-06 | Spoofing | Identity confusion via mutable email | mitigate | Identity keyed on oidc_iss + oidc_sub, never email (D-10) |
| Threat ID | Category | Component | Disposition | Mitigation Plan |
| --------- | ---------------------- | ------------------------------------ | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| T-02-01 | Spoofing | OIDC redirect_uri | mitigate | Authelia validates exact match; OIDC_REDIRECT_URI env must equal the registered URI; OIDC_AUTH_EXTERNAL_URL set so Pangolin Host header cannot forge the redirect (Pitfall 1) |
| T-02-02 | Spoofing | CSRF on /callback | mitigate | @hono/oidc-auth uses PKCE (state + code_verifier); require_pkce true, S256 in Authelia client |
| T-02-03 | Tampering | OIDC session JWT cookie | mitigate | Cookie signed with OIDC_AUTH_SECRET (32+ char), httpOnly + Secure + SameSite; verified every request |
| T-02-04 | Information Disclosure | Refresh token / client_secret | mitigate | Backend-only (D-12); never serialized to frontend; not logged; OIDC_CLIENT_SECRET is the plain secret in env, never committed |
| T-02-05 | Elevation of Privilege | /api/\* without auth | mitigate | oidcAuthMiddleware mounted on /api/\*; no guest access (ASVS V4) |
| T-02-06 | Spoofing | Identity confusion via mutable email | mitigate | Identity keyed on oidc_iss + oidc_sub, never email (D-10) |
</threat_model>
<verification>
@@ -185,11 +190,12 @@ New env vars: `OIDC_AUTH_SECRET`, `OIDC_ISSUER`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_
</verification>
<success_criteria>
- AUTH-01: unauthenticated /api/* redirects to Authelia; login lands authenticated (verified live in Plan 04)
- AUTH-01: unauthenticated /api/\* redirects to Authelia; login lands authenticated (verified live in Plan 04)
- AUTH-02: session persists via backend refresh-token rotation (no iframe)
- AUTH-03: stable identity (iss+sub) + stable distinct per-member color, asserted by unit tests
- /api/me returns the member; PWA shell shows name + color
</success_criteria>
</success_criteria>
<output>
Create `.planning/phases/01-foundation-broker-spike/01-02-SUMMARY.md` when done. Include the Authelia client registration YAML for the operator.