openova/products
e3mrah a8aceacf66
fix(ui): SPA route-guard probes /whoami before bouncing to /login (qa-loop iter-2) (#1203)
When the operator has a valid HttpOnly catalyst_session cookie but no
JS-side `catalyst:authed` sessionStorage marker (fresh tab, refresh
after sessionStorage cleared, deep-link paste into a fresh window),
the synchronous rootBeforeLoad gate redirected them to /login despite
holding a valid session. Caught on console.omantel.biz when deep-link
loads of /dashboard from a sibling tab kept bouncing back to the PIN
page even after a successful PIN verify in another tab.

Root cause: hasCatalystSession() reads sessionStorage only — the
catalyst_session cookie is HttpOnly so JS cannot see it. The marker is
set by VerifyPinPage on PIN verify and SovereignConsoleLayout on
whoami 200, but a fresh-tab navigation neither runs VerifyPinPage nor
mounts the layout before the gate fires, so the gate never sees the
operator as authed.

Fix: keep the sync fast-path (marker present → allow), but on missing
marker fall through to an authoritative GET /api/v1/whoami. On 200
cache the marker and allow through. On 401 redirect to /login with
deep-link preserved as ?next=. On 5xx/network error fail open so the
layout's own probe surfaces the failure with proper context.

Per memory feedback_per_issue_playwright_verification.md: live-verified
the full PIN flow + 6 deep-link routes (/dashboard, /cloud, /apps,
/jobs, /users, /settings) on console.omantel.biz both before and after
the fix. The closed-session hard gate
(session_2026_05_09_closed_unverified.md) is satisfied: incognito
PIN flow → /dashboard renders fully + 5 sibling surfaces render.

Files:
- products/catalyst/bootstrap/ui/src/app/auth-gate.ts
  + probeWhoamiAndCacheMarker(): authoritative async cookie check
- products/catalyst/bootstrap/ui/src/app/router.tsx
  rootBeforeLoad async; falls through to whoami probe when marker missing
- products/catalyst/bootstrap/ui/src/app/auth-gate.test.ts
  +5 tests covering 200/401/5xx/network/credentials-include

Refs: qa-loop iter-2 cluster spa-route-guard-rejects-pin-session
Refs: session_2026_05_09_closed_unverified.md

Co-authored-by: hatiyildiz <hati.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 17:07:12 +04:00
..
axon feat(axon): make qwen3-coder thinking mode toggleable via request parameter 2026-04-26 09:20:33 +02:00
catalyst fix(ui): SPA route-guard probes /whoami before bouncing to /login (qa-loop iter-2) (#1203) 2026-05-09 17:07:12 +04:00
continuum feat(continuum): F — dry-run report + post-switchover health check + audit-emit coverage (slice F-1+F-2+F-3, #1101) (#1161) 2026-05-09 08:33:37 +04:00
cortex docs(pass-52): bundled date-sweep + cross-component namespace clean; knative clean 2026-04-28 00:37:21 +02:00
dmz-vcluster feat(epic-5): NetBird mesh + ClusterMesh activator + DMZ vCluster scaffolds (#1100) (#1171) 2026-05-09 12:14:56 +04:00
fabric docs(seaweedfs+guacamole): replace MinIO with SeaweedFS as unified S3 encapsulation; add Guacamole to bp-relay 2026-04-28 10:23:46 +02:00
fingate docs(pass-52): bundled date-sweep + cross-component namespace clean; knative clean 2026-04-28 00:37:21 +02:00
relay docs(seaweedfs+guacamole): replace MinIO with SeaweedFS as unified S3 encapsulation; add Guacamole to bp-relay 2026-04-28 10:23:46 +02:00