From 1596bb287ea41a72b9807422115548a5247a0b58 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Fri, 29 May 2026 12:19:51 -0700 Subject: [PATCH] fix(dashboard): chat tab works in gated (OAuth) mode (#34793) The Chat/TUI dashboard tab showed a false "Session token unavailable" error and never rendered the terminal whenever the dashboard ran in gated mode (OAuth auth gate active, --insecure not set), even though the user was fully authenticated and every other tab worked. Two checks in ChatPage.tsx gated purely on window.__HERMES_SESSION_TOKEN__, which the server intentionally omits in gated mode (web_server.py only injects __HERMES_AUTH_REQUIRED__=true there; the SPA is expected to use cookie auth + a single-use WS ticket). buildWsAuthParam() already resolves WS auth correctly for both modes, but the early bail prevented the effect from ever reaching it. Both checks now also honor __HERMES_AUTH_REQUIRED__: the banner no longer fires and the xterm/WS effect no longer bails in gated mode. Reported-by: wbrione Closes #34755 --- web/src/pages/ChatPage.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/web/src/pages/ChatPage.tsx b/web/src/pages/ChatPage.tsx index 64886765c..af993c670 100644 --- a/web/src/pages/ChatPage.tsx +++ b/web/src/pages/ChatPage.tsx @@ -119,8 +119,13 @@ export default function ChatPage({ isActive = true }: { isActive?: boolean }) { const [searchParams, setSearchParams] = useSearchParams(); // Lazy-init: the missing-token check happens at construction so the effect // body doesn't have to setState (React 19's set-state-in-effect rule). + // In gated (OAuth) mode the server intentionally omits the session token — + // the SPA authenticates the WS via a single-use ticket (buildWsAuthParam), + // so a missing token there is expected, not an error. const [banner, setBanner] = useState(() => - typeof window !== "undefined" && !window.__HERMES_SESSION_TOKEN__ + typeof window !== "undefined" && + !window.__HERMES_SESSION_TOKEN__ && + !window.__HERMES_AUTH_REQUIRED__ ? "Session token unavailable. Open this page through `hermes dashboard`, not directly." : null, ); @@ -273,8 +278,11 @@ export default function ChatPage({ isActive = true }: { isActive?: boolean }) { if (!host) return; const token = window.__HERMES_SESSION_TOKEN__; + const gated = !!window.__HERMES_AUTH_REQUIRED__; // Banner already initialised above; just bail before wiring xterm/WS. - if (!token) { + // In gated mode the token is absent by design — buildWsAuthParam() mints + // a WS ticket instead, so don't bail; let the effect reach that path. + if (!token && !gated) { return; } @@ -876,5 +884,6 @@ export default function ChatPage({ isActive = true }: { isActive?: boolean }) { declare global { interface Window { __HERMES_SESSION_TOKEN__?: string; + __HERMES_AUTH_REQUIRED__?: boolean; } }