4.9 KiB
phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
| phase | plan | subsystem | tags | dependency_graph | tech_stack | key_files | decisions | metrics | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| quick-260606-tv8 | 01 | auth-entry |
|
|
|
|
|
|
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:
- The guard intercepts and 302s to Authelia.
- After login, Authelia POSTs to
/callback; the middleware sets acontinuecookie pointing to/api/login. - 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()— setssessionStorage['familysync.loginRedirectAttempted']and assignswindow.location.href = '/api/login'on first call; returnsfalseon subsequent calls (loop guard). Guarded againstwindow/sessionStorageunavailability.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: callsmaybeRedirectToLogin(). If first attempt, page navigates away. If already attempted, falls through to the existing "Sign-in required" branch. - On
meQuery.isSuccess: callsclearLoginRedirect()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 --noEmitclean. - Frontend:
vitest run→ 131/131 tests pass (11 test files);tsc --noEmitclean. - Grep confirms
/api/loginappears inapps/api/src/index.ts(route) andapps/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: