From 890f63bf7649660a196ceabdeb642490dcfcecdc Mon Sep 17 00:00:00 2001 From: Steve Krisjanovs Date: Wed, 1 Jul 2026 10:47:43 -0400 Subject: [PATCH] fix(web): redirect to sign-in on 401, instead of failing silently MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to fix/loopback-auth-bypass. That fix made 401 a real, reachable response for the picker's own client JS for the first time — before it, a request from a trusted-looking client essentially never got a 401, so nothing in the browser code had to handle it. Two client-side gaps as a result: - The picker's poll loop (GET /api/sessions every 3s) just caught the resulting error and flipped the status dot to "offline" forever, with no indication the real problem is "sign in again" and no way to get there from the page. - The session-attach page's WS reconnect loop had the same problem, but worse: browsers don't expose a rejected WebSocket handshake's HTTP status at all, so there's no way to even detect "this failed because of auth" from the WS event alone. Fix: install a global fetch() wrapper in both the picker's script and the session-attach page's script — any 401 redirects to /login?returnTo= instead of letting the caller's existing catch block just show a generic failure. For the WS case, the reconnect loop's onclose handler now also fires a throwaway authed fetch (GET /api/sessions) alongside scheduling the reconnect; if the session is actually invalid, that fetch 401s and the wrapper redirects the whole page, tearing down the doomed reconnect loop within one cycle instead of retrying forever. Verified live: loaded the picker with a valid v2 session cookie, cleared the cookie mid-session (simulating expiry/revocation while a tab stays open), and confirmed the next poll cycle (~3s) navigated the browser to /login?returnTo=%2F with the correct page title, rather than sitting on "offline". Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01Au8T9RPCb6wbgiEecQrBfZ --- packages/llmux/src/daemon/web/server.ts | 43 +++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/packages/llmux/src/daemon/web/server.ts b/packages/llmux/src/daemon/web/server.ts index 00f17eb..5f64112 100644 --- a/packages/llmux/src/daemon/web/server.ts +++ b/packages/llmux/src/daemon/web/server.ts @@ -794,6 +794,25 @@ ${renderHeader({