diff --git a/.planning/STATE.md b/.planning/STATE.md index 16c8aea..516ad60 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -100,6 +100,12 @@ Recent decisions affecting current work: - Phase 2/3 dev: build behind a documented dev-auth bypass until Gate 2 deploy (D-14). - Phase 5: iOS push subscriptions silently revoked after 3 silent pushes. Subscription health-check and event.waitUntil() are mandatory from day one. +### Quick Tasks Completed + +| # | Description | Date | Commit | Directory | +|---|-------------|------|--------|-----------| +| 260606-tv8 | Fix missing sign-in redirect in the PWA (Phase 03 auth-entry gap from Gate 2): guarded /api/login → / + full-page redirect on unauthenticated fetchMe | 2026-06-07 | 7c6531f | [260606-tv8-fix-missing-sign-in-redirect-in-the-pwa-](./quick/260606-tv8-fix-missing-sign-in-redirect-in-the-pwa-/) | + ## Deferred Items | Category | Item | Status | Deferred At | @@ -113,6 +119,6 @@ Recent decisions affecting current work: ## Session Continuity -Last session: 2026-06-05T22:48:01.588Z -Stopped at: Completed 03-05-PLAN.md +Last session: 2026-06-07T01:30:00.000Z +Stopped at: Gate 2 live bring-up — real OIDC login working; quick task 260606-tv8 fixed the missing sign-in redirect; rebuild + browser re-verify pending Resume file: None diff --git a/.planning/quick/260606-tv8-fix-missing-sign-in-redirect-in-the-pwa-/260606-tv8-SUMMARY.md b/.planning/quick/260606-tv8-fix-missing-sign-in-redirect-in-the-pwa-/260606-tv8-SUMMARY.md new file mode 100644 index 0000000..5e7fedf --- /dev/null +++ b/.planning/quick/260606-tv8-fix-missing-sign-in-redirect-in-the-pwa-/260606-tv8-SUMMARY.md @@ -0,0 +1,101 @@ +--- +phase: quick-260606-tv8 +plan: "01" +subsystem: auth-entry +tags: [auth, pwa, oidc, redirect, one-shot-guard] +dependency_graph: + requires: [] + provides: [AUTH-ENTRY-01] + affects: [apps/api/src/index.ts, apps/pwa/src/lib/loginRedirect.ts, apps/pwa/src/components/CalendarShell.tsx] +tech_stack: + added: [] + patterns: + - sessionStorage one-shot redirect guard for OIDC re-auth + - top-level nav to /api/login bypasses XHR CORS block on Authelia 302 +key_files: + created: + - apps/api/tests/routes/login.test.ts + - apps/pwa/src/lib/loginRedirect.ts + - apps/pwa/src/lib/loginRedirect.test.ts + modified: + - apps/api/src/index.ts + - apps/pwa/src/api/client.ts + - apps/pwa/src/components/CalendarShell.tsx +decisions: + - "GET /api/login placed after OIDC guard with bare app.get (not app.route) — single redirect, no router needed" + - "sessionStorage chosen over localStorage for the one-shot flag — flag is per-tab and clears on tab close, preventing stale block across sessions" + - "useEffect dependencies are meQuery.isError and meQuery.isSuccess (not the query object) — stable boolean flags, no unnecessary re-invocations" +metrics: + duration: "5 minutes" + completed_date: "2026-06-07" + tasks_completed: 3 + files_changed: 6 +--- + +# Quick Task 260606-tv8: Fix Missing Sign-in Redirect in the PWA — Summary + +**One-liner:** Restored the OIDC auth entry path by adding a guarded `/api/login` backend route and a sessionStorage one-shot redirect helper wired into CalendarShell, so unauthenticated PWA loads trigger a full-page navigation instead of a dead-end "Sign-in required" message. + +## What Was Built + +### Task 1: GET /api/login backend route + tests (commit 237ec49) + +Added `app.get('/api/login', (c) => c.redirect('/'))` in `apps/api/src/index.ts`, placed after the OIDC guard block. When an unauthenticated user navigates to `/api/login` as a top-level browser request: +1. The guard intercepts and 302s to Authelia. +2. After login, Authelia POSTs to `/callback`; the middleware sets a `continue` cookie pointing to `/api/login`. +3. The browser follows the cookie back to `/api/login` (now authenticated); the handler 302s to `/` and the SPA boots. + +Under `DEV_AUTH_BYPASS=true`, the guard is not mounted so the handler fires directly and redirects to `/`. + +Created `apps/api/tests/routes/login.test.ts` (3 tests, mirrors `me.test.ts` pattern) covering both the bypass path and the OIDC-passthrough path. + +### Task 2: One-shot login-redirect helper + tests (commit 6dc9ccd) + +Created `apps/pwa/src/lib/loginRedirect.ts` exporting: +- `maybeRedirectToLogin()` — sets `sessionStorage['familysync.loginRedirectAttempted']` and assigns `window.location.href = '/api/login'` on first call; returns `false` on subsequent calls (loop guard). Guarded against `window`/`sessionStorage` unavailability. +- `clearLoginRedirect()` — removes the flag, allowing future re-auth redirects. + +Created `apps/pwa/src/lib/loginRedirect.test.ts` (5 tests) covering first-call redirect, one-shot no-op, and clear+retry. + +Updated `apps/pwa/src/api/client.ts` to remove the false claim that "the browser will follow the 302 redirect to Authelia automatically" — fetch/XHR cannot follow cross-origin 302s to Authelia (CORS-blocked). The comment now accurately describes that top-level navigation via `/api/login` is required. + +### Task 3: CalendarShell meQuery wiring (commit c2e0ab1) + +Added two `useEffect` hooks in `CalendarShell.tsx`: +- On `meQuery.isError`: calls `maybeRedirectToLogin()`. If first attempt, page navigates away. If already attempted, falls through to the existing "Sign-in required" branch. +- On `meQuery.isSuccess`: calls `clearLoginRedirect()` so a later session expiry can redirect again. + +The existing `if (meQuery.isError) { return }` block is retained as the fall-through for the already-attempted case. + +## Deviations from Plan + +None — plan executed exactly as written. + +## Verification Results + +- Backend: `vitest run` → 95/95 tests pass (13 test files); `tsc --noEmit` clean. +- Frontend: `vitest run` → 131/131 tests pass (11 test files); `tsc --noEmit` clean. +- Grep confirms `/api/login` appears in `apps/api/src/index.ts` (route) and `apps/pwa/src/lib/loginRedirect.ts` (helper). + +## Known Stubs + +None. + +## Threat Flags + +None — no new network endpoints (the `/api/login` route is behind the existing `/api/*` OIDC guard), no new auth paths (redirect target is Authelia, same as before), no schema changes. + +## Self-Check: PASSED + +Files exist: +- apps/api/src/index.ts (modified) +- apps/api/tests/routes/login.test.ts (created) +- apps/pwa/src/lib/loginRedirect.ts (created) +- apps/pwa/src/lib/loginRedirect.test.ts (created) +- apps/pwa/src/api/client.ts (modified) +- apps/pwa/src/components/CalendarShell.tsx (modified) + +Commits: +- 237ec49: feat(260606-tv8-01): add guarded GET /api/login route + tests +- 6dc9ccd: feat(260606-tv8-01): add one-shot login-redirect helper + tests; fix client.ts comment +- c2e0ab1: feat(260606-tv8-01): wire login redirect into CalendarShell meQuery handling