mirror of
https://github.com/ruvnet/RuView
synced 2026-07-25 17:51:48 +00:00
main
2 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9b9754778f |
fix(ui): service worker cached /oauth/status, freezing browser sign-in
Browser sign-in worked, but the settings panel kept offering "Sign in with
Cognitum" afterwards; only a hard reload showed the true state. The server was
correct throughout.
Root cause: `ui/sw.js` routed cache-first as its CATCH-ALL for every path
outside `/api/` and `/health/`. `/oauth/status` therefore had its first
(signed-out) response stored in the Cache API and replayed to the page forever.
The Cache API is not the HTTP cache and ignores `Cache-Control` entirely, so the
`no-store, no-cache, must-revalidate` the server already sends could not prevent
it. A hard reload bypasses the service worker, which is why that alone appeared
to fix it, and why signing out re-poisoned the entry.
Fixes, in order of blast radius:
- `/oauth/` is never handled by the worker. Not network-first — that still
writes a copy, which would be replayed the moment the server is briefly
unreachable, silently reinstating a stale sign-in state.
- Cache-first is now an ALLOWLIST (navigations and static asset extensions)
rather than the catch-all. This is the underlying defect: any endpoint added
outside `/api/` was frozen on its first response. Unrecognised paths now go
to the network untouched.
- `CACHE_NAME` bumped to `ruview-v2`, so `activate` evicts the poisoned
`ruview-v1` from browsers that already ran the old worker. Verified: the
cache list went from `[ruview-v1]` to `[ruview-v2]` on update.
- Shell lookups use `ignoreSearch` and store a search-less key, so the
`?signed_in=<ms>` the callback redirects to does not mint a fresh, never-hit
cache entry per sign-in.
Also: re-check sign-in state on `pageshow` (bfcache restore, where no script
re-runs) and on `visibilitychange` (signed in or out in another tab). Opening
the panel alone is not sufficient — it may already be open.
Verification, in a real browser driven end-to-end:
- Reproduced with a valid session cookie: server returned `signed_in: true` to
curl while the page's own fetch got a cached `signed_in: false`, and the
request never appeared in the server log at all.
- Confirmed the cached entry existed: `caches.open('ruview-v1').match(
'/oauth/status')` returned the signed-out body.
- After the fix, on a NORMAL (not hard) reload the panel reads
"Signed in as <account> - sensing:read" with Sign out shown.
Tests: `ui/sw.test.mjs` loads the real `sw.js` with stubbed worker globals and
asserts the routing decision per path. 4 of its 10 tests fail against the
pre-fix worker and pass after; the other 6 pin pre-existing guards (non-GET,
websocket upgrade, cross-origin, API paths, static assets, navigation) and pass
in both, so they track behaviour rather than the rewrite.
CI: adds a `ui-tests` job. Nothing ran the UI JavaScript before, so both this
suite and the ADR-272 ws-ticket suite would have rotted unexecuted — which is
the same blind spot that let this defect ship.
Removes the temporary `/oauth/status` cookie logging and the panel console
diagnostic added while tracking this down.
Co-Authored-By: Ruflo & AQE
|
||
|
|
9299a3b137 |
feat(ui): Sign in with Cognitum, zero-config session secret, executed JS tests
Three loose ends from the browser sign-in work. --- 1. The last mile: a button --- The server endpoints existed but nothing in ui/ linked to them, so the feature was unreachable. QuickSettings gains a "Cognitum Account" panel that renders from `GET /oauth/status` and offers Sign in / Sign out. `/oauth/status` is a new endpoint and is deliberately UNGATED — a signed-OUT browser cannot ask a gated API whether sign-in is available. It returns only capability flags and, when a session exists, who it belongs to. Never a credential. The panel distinguishes four states rather than showing a button that might 404: signed in; sign-in available; auth on but OAuth off (points at the existing static-token panel); no auth required. A 404 from /oauth/status means a server predating this work and says so plainly. Sign-in is a full-page navigation, not fetch(): the server replies 302 and the browser must follow it carrying the transaction cookie. An XHR would follow the redirect invisibly and land nowhere. --- 2. RUVIEW_SESSION_SECRET no longer required --- Previously `/oauth/start` returned 503 unless an operator invented a secret — a footgun, since they set RUVIEW_OAUTH_ISSUER, expect sign-in, and get a 503 naming an env var they have never heard of. Now resolved in order: env var, then `<data_dir>/session-secret`, then generate one and persist it 0600 (temp file, chmod before rename — the discipline used for the CLI's credentials). Persisted rather than in-memory so a restart does not silently sign everyone out. The env var still wins, which is what a multi-instance deployment needs: several servers must share a secret or a session issued by one is rejected by the next. If the file cannot be written we log the reason and continue with an in-memory secret rather than refusing sign-in outright. Verified with NO configuration at all: secret generated, file mode 0600, /oauth/start returns 302, /oauth/status reports browser_signin: true. --- 3. The JavaScript is now executed by tests --- `ui/services/ws-ticket.test.mjs`, 9 tests, node:test built-in — no new dependency and no package.json needed. Run: `node --test ui/services/`. Covers: no token means no fetch and an unchanged URL; the bearer reaches the Authorization header and NEVER the URL; `?` vs `&` when a query already exists; ticket URL-encoding; 404 treated as a pre-ADR-272 server (the property that lets one UI work against old and new servers, and therefore lets the legacy escape hatch be removed later); 503 and network failure swallowed rather than breaking the connect path; and that a fresh ticket is minted per call, since tickets are single-use and caching one fails on the second reconnect. STATED PRECISELY, because the distinction matters: this EXECUTES the module in Node with stubbed fetch/localStorage. It is more than the `node --check` it replaces and less than a browser — no real WebSocket upgrade, no real cookies, no page wiring. "The UI JavaScript has never been run" is no longer true of this module. "Browser-tested" still is not. Tests: 547 sensing-server lib, 5 wiring integration, 87 ruview-auth, 9 JS. Co-Authored-By: Ruflo & AQE |