Files
Lucas Berger 982438dc10 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.
2026-06-11 20:35:18 -04:00

5.0 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
auth
pwa
oidc
redirect
one-shot-guard
requires provides affects
AUTH-ENTRY-01
apps/api/src/index.ts
apps/pwa/src/lib/loginRedirect.ts
apps/pwa/src/components/CalendarShell.tsx
added patterns
sessionStorage one-shot redirect guard for OIDC re-auth
top-level nav to /api/login bypasses XHR CORS block on Authelia 302
created modified
apps/api/tests/routes/login.test.ts
apps/pwa/src/lib/loginRedirect.ts
apps/pwa/src/lib/loginRedirect.test.ts
apps/api/src/index.ts
apps/pwa/src/api/client.ts
apps/pwa/src/components/CalendarShell.tsx
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
duration completed_date tasks_completed files_changed
5 minutes 2026-06-07 3 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