Milestone v1.0: FamilySync MVP #1

Merged
luckberg merged 376 commits from gsd/v1.0-milestone into main 2026-06-10 17:39:19 -04:00
2 changed files with 109 additions and 2 deletions
Showing only changes of commit 874f23de2f - Show all commits
+8 -2
View File
@@ -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
@@ -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 <Sign-in required /> }` 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