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:
+28
-19
@@ -16,27 +16,27 @@ must_haves:
|
||||
- "When @hono/oidc-auth produces a valid session this request (c.get('oidcAuthJwt') is truthy), the response re-sets the oidc-auth cookie with a persistent Max-Age so the browser keeps it across PWA/tab close"
|
||||
- "When no valid session JWT is on context (logged-out / deleted / never-set request, c.get('oidcAuthJwt') is falsy), NO oidc-auth Set-Cookie is emitted — the middleware never resurrects a deleted or absent cookie (the critical correctness/security guard)"
|
||||
- "The re-issued cookie carries httpOnly:true, secure:true, sameSite:'Lax', and the domain attribute only when OIDC_COOKIE_DOMAIN is set, mirroring the library's conditional-domain logic"
|
||||
- "pnpm --filter @familysync/api typecheck exits 0"
|
||||
- 'pnpm --filter @familysync/api typecheck exits 0'
|
||||
artifacts:
|
||||
- path: "apps/api/src/auth/persistSessionCookie.ts"
|
||||
provides: "persistSessionCookie() Hono MiddlewareHandler that upgrades the session-scoped oidc-auth cookie to a persistent one"
|
||||
exports: ["persistSessionCookie"]
|
||||
- path: 'apps/api/src/auth/persistSessionCookie.ts'
|
||||
provides: 'persistSessionCookie() Hono MiddlewareHandler that upgrades the session-scoped oidc-auth cookie to a persistent one'
|
||||
exports: ['persistSessionCookie']
|
||||
min_lines: 25
|
||||
- path: "apps/api/tests/auth/persistSessionCookie.test.ts"
|
||||
provides: "Vitest unit tests for the persist + guard behaviors"
|
||||
contains: "persistSessionCookie"
|
||||
- path: "apps/api/src/index.ts"
|
||||
provides: "Mounting of persistSessionCookie immediately after oidcAuthMiddleware inside the !devBypassActive block"
|
||||
contains: "persistSessionCookie"
|
||||
- path: 'apps/api/tests/auth/persistSessionCookie.test.ts'
|
||||
provides: 'Vitest unit tests for the persist + guard behaviors'
|
||||
contains: 'persistSessionCookie'
|
||||
- path: 'apps/api/src/index.ts'
|
||||
provides: 'Mounting of persistSessionCookie immediately after oidcAuthMiddleware inside the !devBypassActive block'
|
||||
contains: 'persistSessionCookie'
|
||||
key_links:
|
||||
- from: "apps/api/src/index.ts"
|
||||
to: "apps/api/src/auth/persistSessionCookie.ts"
|
||||
- from: 'apps/api/src/index.ts'
|
||||
to: 'apps/api/src/auth/persistSessionCookie.ts'
|
||||
via: "import + app.use('/api/*', persistSessionCookie()) inside if (!devBypassActive)"
|
||||
pattern: "persistSessionCookie\\(\\)"
|
||||
- from: "apps/api/src/auth/persistSessionCookie.ts"
|
||||
to: "oidcAuthJwt context var (set by @hono/oidc-auth)"
|
||||
- from: 'apps/api/src/auth/persistSessionCookie.ts'
|
||||
to: 'oidcAuthJwt context var (set by @hono/oidc-auth)'
|
||||
via: "c.get('oidcAuthJwt') read; cookie re-issued only when truthy"
|
||||
pattern: "oidcAuthJwt"
|
||||
pattern: 'oidcAuthJwt'
|
||||
---
|
||||
|
||||
<objective>
|
||||
@@ -63,11 +63,17 @@ Output: New middleware file + its tests, wired into index.ts.
|
||||
@apps/api/tests/auth/devBypass.test.ts
|
||||
|
||||
# Ground truth on the library's cookie + context behavior (do NOT edit the library):
|
||||
|
||||
# - @hono/oidc-auth/dist/index.js sets a session-scoped cookie via
|
||||
# setCookie(c, OIDC_COOKIE_NAME, session_jwt, { path, httpOnly:true, secure:true [, domain if OIDC_COOKIE_DOMAIN] })
|
||||
# with NO maxAge/expires, then immediately calls c.set('oidcAuthJwt', session_jwt).
|
||||
|
||||
# setCookie(c, OIDC_COOKIE_NAME, session_jwt, { path, httpOnly:true, secure:true [, domain if OIDC_COOKIE_DOMAIN] })
|
||||
|
||||
# with NO maxAge/expires, then immediately calls c.set('oidcAuthJwt', session_jwt).
|
||||
|
||||
# - oidcAuthJwt is set ONLY on requests where a valid session is created/refreshed.
|
||||
# Logged-out / no-session requests do NOT set it. This is the guard signal.
|
||||
|
||||
# Logged-out / no-session requests do NOT set it. This is the guard signal.
|
||||
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
@@ -99,6 +105,7 @@ Output: New middleware file + its tests, wired into index.ts.
|
||||
4. Then `await next()`.
|
||||
|
||||
Keep it tiny. Do NOT import or call into @hono/oidc-auth. Do NOT add new dependencies (hono/cookie ships with hono, already a dependency). Do NOT place fenced code blocks in production comments.
|
||||
|
||||
</action>
|
||||
<verify>
|
||||
<automated>pnpm --filter @familysync/api typecheck</automated>
|
||||
@@ -123,6 +130,7 @@ Output: New middleware file + its tests, wired into index.ts.
|
||||
- Test B (guard path — the security property): build a Hono app that does NOT set oidcAuthJwt, run `persistSessionCookie()`, hit a GET /api/test. Assert `res.headers.get('set-cookie')` is null OR does not contain the oidc-auth cookie name — i.e. no resurrection when there is no session JWT.
|
||||
- Read the cookie NAME the same way the implementation does (`process.env.OIDC_COOKIE_NAME ?? 'oidc-auth'`) so the test stays correct if the env override is set; default 'oidc-auth' otherwise.
|
||||
Do NOT call the real @hono/oidc-auth in the test — set the context var directly. Do NOT add DB/integration setup (keep it a pure unit test so it runs under the tests/auth/ filter without MariaDB).
|
||||
|
||||
</action>
|
||||
<verify>
|
||||
<automated>pnpm --filter @familysync/api typecheck && pnpm --filter @familysync/api exec vitest run tests/auth/</automated>
|
||||
@@ -140,10 +148,11 @@ Output: New middleware file + its tests, wired into index.ts.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
|
||||
- A logged-in member who closes the PWA and reopens it later (within OIDC_AUTH_EXPIRES) is NOT bounced to Authelia — the oidc-auth cookie now has a Max-Age and persists.
|
||||
- A logged-out / no-session request never receives an oidc-auth Set-Cookie from this middleware (no resurrection).
|
||||
- No changes to @hono/oidc-auth, package.json, or .env.
|
||||
</success_criteria>
|
||||
</success_criteria>
|
||||
|
||||
<notes>
|
||||
- Tuning the session lifetime is config-only, no code change: raising `OIDC_AUTH_EXPIRES` in .env (e.g. `2592000` for 30 days) extends both the server-side JWT lifetime AND the persisted cookie's Max-Age — the new middleware reads `OIDC_AUTH_EXPIRES` for maxAge automatically. The effective ceiling is still bounded by Authelia's refresh_token_lifespan.
|
||||
|
||||
+7
-4
@@ -37,14 +37,15 @@ metrics:
|
||||
|
||||
## Tasks Completed
|
||||
|
||||
| Task | Name | Commit | Files |
|
||||
|------|------|--------|-------|
|
||||
| 1 | Create persistSessionCookie() middleware | aabcb5d | apps/api/src/auth/persistSessionCookie.ts |
|
||||
| 2 | Wire into index.ts + unit tests | 8343fad | apps/api/src/index.ts, apps/api/tests/auth/persistSessionCookie.test.ts |
|
||||
| Task | Name | Commit | Files |
|
||||
| ---- | ---------------------------------------- | ------- | ----------------------------------------------------------------------- |
|
||||
| 1 | Create persistSessionCookie() middleware | aabcb5d | apps/api/src/auth/persistSessionCookie.ts |
|
||||
| 2 | Wire into index.ts + unit tests | 8343fad | apps/api/src/index.ts, apps/api/tests/auth/persistSessionCookie.test.ts |
|
||||
|
||||
## Verification Results
|
||||
|
||||
### typecheck
|
||||
|
||||
```
|
||||
$ pnpm --filter @familysync/api typecheck
|
||||
$ tsc --noEmit
|
||||
@@ -52,6 +53,7 @@ $ tsc --noEmit
|
||||
```
|
||||
|
||||
### vitest run tests/auth/
|
||||
|
||||
```
|
||||
RUN v4.1.8 /home/luc/Projects/familysync/apps/api
|
||||
|
||||
@@ -62,6 +64,7 @@ $ tsc --noEmit
|
||||
```
|
||||
|
||||
All 3 auth test files pass (devBypass, user, persistSessionCookie). 14/14 tests green including:
|
||||
|
||||
- Test A (persist path): truthy oidcAuthJwt → Set-Cookie with Max-Age, SameSite=Lax, HttpOnly, Secure
|
||||
- Test B (guard path): absent oidcAuthJwt → no oidc-auth Set-Cookie emitted
|
||||
- Guard variant: empty string oidcAuthJwt → no resurrection
|
||||
|
||||
+28
-28
@@ -17,39 +17,39 @@ overrides_applied: 0
|
||||
|
||||
### Observable Truths
|
||||
|
||||
| # | Truth | Status | Evidence |
|
||||
|---|-------|--------|----------|
|
||||
| 1 | Truthy oidcAuthJwt → response re-sets oidc-auth cookie with Max-Age, SameSite=Lax, HttpOnly, Secure | VERIFIED | `persistSessionCookie.ts` lines 47–76: guard passes, `setCookie(c, name, jwt, { httpOnly:true, secure:true, sameSite:'Lax', maxAge, ... })` called before `await next()` |
|
||||
| 2 | Falsy/absent oidcAuthJwt → NO oidc-auth Set-Cookie emitted (no resurrection guard) | VERIFIED | Lines 47–50: `if (!jwt) { await next(); return }` — hard early exit before any `setCookie` call; covered by Test B (absent) and guard variant (empty string), both passing |
|
||||
| 3 | Cookie carries httpOnly:true, secure:true, sameSite:'Lax'; domain included only when OIDC_COOKIE_DOMAIN set | VERIFIED | Options object built lines 63–69 (always includes httpOnly/secure/sameSite); `if (process.env.OIDC_COOKIE_DOMAIN)` guard at line 71 adds domain key only when env var is set — key absent (not undefined) when unset |
|
||||
| 4 | `pnpm --filter @familysync/api typecheck` exits 0 | VERIFIED | Run output: `$ tsc --noEmit` — exit 0, no diagnostic output |
|
||||
| # | Truth | Status | Evidence |
|
||||
| --- | ----------------------------------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 1 | Truthy oidcAuthJwt → response re-sets oidc-auth cookie with Max-Age, SameSite=Lax, HttpOnly, Secure | VERIFIED | `persistSessionCookie.ts` lines 47–76: guard passes, `setCookie(c, name, jwt, { httpOnly:true, secure:true, sameSite:'Lax', maxAge, ... })` called before `await next()` |
|
||||
| 2 | Falsy/absent oidcAuthJwt → NO oidc-auth Set-Cookie emitted (no resurrection guard) | VERIFIED | Lines 47–50: `if (!jwt) { await next(); return }` — hard early exit before any `setCookie` call; covered by Test B (absent) and guard variant (empty string), both passing |
|
||||
| 3 | Cookie carries httpOnly:true, secure:true, sameSite:'Lax'; domain included only when OIDC_COOKIE_DOMAIN set | VERIFIED | Options object built lines 63–69 (always includes httpOnly/secure/sameSite); `if (process.env.OIDC_COOKIE_DOMAIN)` guard at line 71 adds domain key only when env var is set — key absent (not undefined) when unset |
|
||||
| 4 | `pnpm --filter @familysync/api typecheck` exits 0 | VERIFIED | Run output: `$ tsc --noEmit` — exit 0, no diagnostic output |
|
||||
|
||||
**Score:** 4/4 truths verified
|
||||
|
||||
### Required Artifacts
|
||||
|
||||
| Artifact | Expected | Status | Details |
|
||||
|----------|----------|--------|---------|
|
||||
| `apps/api/src/auth/persistSessionCookie.ts` | persistSessionCookie() MiddlewareHandler; min 25 lines | VERIFIED | 81 lines; exports `persistSessionCookie(): MiddlewareHandler`; imports only `hono` and `hono/cookie` — no new dependencies |
|
||||
| `apps/api/tests/auth/persistSessionCookie.test.ts` | Vitest unit tests; contains "persistSessionCookie" | VERIFIED | 123 lines; 4 tests across 2 describe groups; pure unit test (no MariaDB, no @hono/oidc-auth import) |
|
||||
| `apps/api/src/index.ts` | Mounts persistSessionCookie immediately after oidcAuthMiddleware inside !devBypassActive | VERIFIED | Line 14: import present; lines 52–54: `app.use('/api/*', oidcAuthMiddleware())` followed immediately by `app.use('/api/*', persistSessionCookie())` — both inside `if (!devBypassActive)` block |
|
||||
| Artifact | Expected | Status | Details |
|
||||
| -------------------------------------------------- | ---------------------------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `apps/api/src/auth/persistSessionCookie.ts` | persistSessionCookie() MiddlewareHandler; min 25 lines | VERIFIED | 81 lines; exports `persistSessionCookie(): MiddlewareHandler`; imports only `hono` and `hono/cookie` — no new dependencies |
|
||||
| `apps/api/tests/auth/persistSessionCookie.test.ts` | Vitest unit tests; contains "persistSessionCookie" | VERIFIED | 123 lines; 4 tests across 2 describe groups; pure unit test (no MariaDB, no @hono/oidc-auth import) |
|
||||
| `apps/api/src/index.ts` | Mounts persistSessionCookie immediately after oidcAuthMiddleware inside !devBypassActive | VERIFIED | Line 14: import present; lines 52–54: `app.use('/api/*', oidcAuthMiddleware())` followed immediately by `app.use('/api/*', persistSessionCookie())` — both inside `if (!devBypassActive)` block |
|
||||
|
||||
### Key Link Verification
|
||||
|
||||
| From | To | Via | Status | Details |
|
||||
|------|----|-----|--------|---------|
|
||||
| `apps/api/src/index.ts` | `apps/api/src/auth/persistSessionCookie.ts` | `import { persistSessionCookie } from './auth/persistSessionCookie.js'` + `app.use('/api/*', persistSessionCookie())` inside `if (!devBypassActive)` | VERIFIED | Import at line 14; usage at line 54; ordering correct — line 53 oidcAuthMiddleware, line 54 persistSessionCookie |
|
||||
| `apps/api/src/auth/persistSessionCookie.ts` | oidcAuthJwt context var (@hono/oidc-auth) | `c.get('oidcAuthJwt' as never)` read; cookie re-issued only when truthy | VERIFIED | Line 40: `const jwt = c.get('oidcAuthJwt' as never) as string | undefined`; line 47: falsy guard; no import of @hono/oidc-auth |
|
||||
| From | To | Via | Status | Details |
|
||||
| ------------------------------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
||||
| `apps/api/src/index.ts` | `apps/api/src/auth/persistSessionCookie.ts` | `import { persistSessionCookie } from './auth/persistSessionCookie.js'` + `app.use('/api/*', persistSessionCookie())` inside `if (!devBypassActive)` | VERIFIED | Import at line 14; usage at line 54; ordering correct — line 53 oidcAuthMiddleware, line 54 persistSessionCookie |
|
||||
| `apps/api/src/auth/persistSessionCookie.ts` | oidcAuthJwt context var (@hono/oidc-auth) | `c.get('oidcAuthJwt' as never)` read; cookie re-issued only when truthy | VERIFIED | Line 40: `const jwt = c.get('oidcAuthJwt' as never) as string | undefined`; line 47: falsy guard; no import of @hono/oidc-auth |
|
||||
|
||||
### Behavioral Spot-Checks (Vitest)
|
||||
|
||||
| Behavior | Command | Result | Status |
|
||||
|----------|---------|--------|--------|
|
||||
| Full tests/auth/ suite | `pnpm --filter @familysync/api exec vitest run tests/auth/` | 3 test files, 14 tests, all passed | PASS |
|
||||
| Test A — persist path (Max-Age + SameSite=Lax + HttpOnly + Secure present) | included in suite above | passes | PASS |
|
||||
| Test A variant — same JWT value re-issued unchanged | included in suite above | passes | PASS |
|
||||
| Test B — guard path (absent oidcAuthJwt → no Set-Cookie) | included in suite above | passes | PASS |
|
||||
| Test B variant — empty string oidcAuthJwt → no resurrection | included in suite above | passes | PASS |
|
||||
| Behavior | Command | Result | Status |
|
||||
| -------------------------------------------------------------------------- | ----------------------------------------------------------- | ---------------------------------- | ------ |
|
||||
| Full tests/auth/ suite | `pnpm --filter @familysync/api exec vitest run tests/auth/` | 3 test files, 14 tests, all passed | PASS |
|
||||
| Test A — persist path (Max-Age + SameSite=Lax + HttpOnly + Secure present) | included in suite above | passes | PASS |
|
||||
| Test A variant — same JWT value re-issued unchanged | included in suite above | passes | PASS |
|
||||
| Test B — guard path (absent oidcAuthJwt → no Set-Cookie) | included in suite above | passes | PASS |
|
||||
| Test B variant — empty string oidcAuthJwt → no resurrection | included in suite above | passes | PASS |
|
||||
|
||||
### Anti-Patterns Found
|
||||
|
||||
@@ -57,12 +57,12 @@ None. No TBD/FIXME/XXX markers, no placeholder returns, no empty handlers, no ha
|
||||
|
||||
### Scope Constraint Verification
|
||||
|
||||
| Constraint | Status | Evidence |
|
||||
|------------|--------|----------|
|
||||
| No changes to @hono/oidc-auth | VERIFIED | Only files modified: persistSessionCookie.ts (new), index.ts (import + 2 lines), persistSessionCookie.test.ts (new) |
|
||||
| No changes to package.json | VERIFIED | No package.json in modified files list; only `hono/cookie` used, which ships with the existing `hono` dependency |
|
||||
| No changes to .env | VERIFIED | Not in modified files list |
|
||||
| Cookie set BEFORE await next() | VERIFIED | `setCookie(c, name, jwt, options)` at line 76; `await next()` at line 78 |
|
||||
| Constraint | Status | Evidence |
|
||||
| --------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| No changes to @hono/oidc-auth | VERIFIED | Only files modified: persistSessionCookie.ts (new), index.ts (import + 2 lines), persistSessionCookie.test.ts (new) |
|
||||
| No changes to package.json | VERIFIED | No package.json in modified files list; only `hono/cookie` used, which ships with the existing `hono` dependency |
|
||||
| No changes to .env | VERIFIED | Not in modified files list |
|
||||
| Cookie set BEFORE await next() | VERIFIED | `setCookie(c, name, jwt, options)` at line 76; `await next()` at line 78 |
|
||||
| Middleware NOT mounted under dev bypass | VERIFIED | Both `oidcAuthMiddleware()` and `persistSessionCookie()` registrations are inside `if (!devBypassActive)` block (index.ts lines 51–55) |
|
||||
|
||||
### Human Verification Required
|
||||
|
||||
Reference in New Issue
Block a user