Skip to content

[BFF-FE] Phase B — cookie-based auth code migration #157

Description

@cyberantonz

Part of the BFF cookie-auth migration EPIC.

Replace the oidc-client-ts / sessionStorage / Bearer flow with cookie + /auth/me. Drives the FE PRD rewritten in FE Phase A (#156).

Order (resolved Q5): start once backend Phases 2 and 3 are live (refresh, logout, sessions, CSRF, back-channel). Router Phase 1 should also be in dev by then so /api/* calls work cookie-only.

Scope

Files

File Change
src/app/auth/OidcManager.ts Delete. All OIDC client logic now backend-side.
src/app/effects/authEffects.ts Rewrite. On boot: GET /auth/me. On refresh_at: POST /auth/refresh (leader-only). On 401: window.location = '/auth/login?return_to=...'.
src/app/actions/bootstrapActions.ts Stop decoding JWT. Read user.email etc. from /auth/me response. Reverts the unique_name/sub fallback hack from commit 6aa99ce.
src/app/api/AuthApiService.ts New methods: getMe(), refresh(), logout(), listSessions(), revokeSession(id), revokeAll(), getCsrf(). All requests use credentials: 'include'.
HTTP client (AuthPlugin + friends) Set credentials: 'include' on all fetch calls. Remove all Authorization: Bearer … injection. Add CSRF middleware: read token from auth state cache (primed by /auth/me), echo as X-CSRF-Token on POST/PUT/PATCH/DELETE.
Login UI / route guard Sign-in button: window.location = '/auth/login?return_to=' + encodeURIComponent(location.pathname). Unauth state derived from /auth/me 401.
NEW src/app/auth/AuthTabCoordinator.ts BroadcastChannel('insight-auth') leader election. Only leader fires refresh. Followers receive {expires_at, refresh_at} broadcast and update timers. Fallback to localStorage 'storage' events.
src/app/auth/RestMockPlugin.ts (legacy) Delete the window.__OIDC_CONFIG__ mock. SPA no longer needs OIDC config — all logic backend-side.
docker-entrypoint.sh Remove OIDC_* env-var injection (OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_SCOPES). SPA chart values can drop the corresponding keys.
package.json Remove oidc-client-ts. Remove any JWT-decode lib if only used for token parsing.
vite.config.ts Verify dev proxy routes /auth/* and /api/* to the api-gateway dev port (http://localhost:8080) so cookies remain first-party in dev.

Logic detail

  • Boot flow: SPA calls GET /auth/me. On 200 → app renders, schedule refresh timer at refresh_at. On 401 → redirect to /auth/login?return_to=....
  • Refresh timer: setTimeout(refresh_at - now). On fire, leader calls POST /auth/refresh with X-CSRF-Token. On success, broadcast {expires_at, refresh_at} and reschedule. On 401 → broadcast logout, redirect to login.
  • Leader election: First tab to join channel claims leadership. On leader unload, next tab takes over (heartbeat via BroadcastChannel). Followers honor leader's refresh broadcasts and do not fire their own timer.
  • API 401 handler: Any /api/* returning 401 → redirect to /auth/login?return_to=.... NEVER auto-refresh on 401 — refresh is server-scheduled.
  • CSRF: Token primed from /auth/me. Refreshed via GET /auth/csrf if a state-changing request gets 403 with WWW-Authenticate: csrf-required.
  • Logout: Button → POST /auth/logout → response {end_session_url}window.location = end_session_url.

Out of scope

  • Sessions management UI (/auth/sessions endpoints) — file as a follow-up issue if/when the UX exists. Backend ships it; SPA can consume it later.
  • Impersonation flow changes — backend dev-fake-OIDC stub covers dev needs; the existing impersonation feature (persisted in sessionStorage per commit 07ddc69) is orthogonal and stays as-is for now.

Acceptance

  • Cold load in incognito → SPA redirects to backend /auth/login. After IdP flow, lands on the original path.
  • document.cookie shows only __Host-sid (HttpOnly, can't read from JS — verify via DevTools Network tab cookie inspector).
  • sessionStorage and localStorage contain NO token data. (Old oidc.user:* keys are gone.)
  • Open two tabs → both authenticated; only one tab fires /auth/refresh per round (verify in Network tab).
  • Logout from one tab → other tab redirects to login on next interaction (401 from /api/*).
  • POST to a state-changing API → X-CSRF-Token header present. Missing → 403 from backend.
  • Remove oidc-client-ts from package.jsonnpm install + build succeeds; bundle size drops measurably.
  • Manual smoke against cargo run -p api-gateway --features dev-auth + dev-fake-OIDC stub: full login/refresh/logout loop with INSIGHT_DEV_USER_EMAIL user.
  • All existing E2E tests pass (or get updated; expect mostly login flow tests to change).

Dependencies

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions