fix(#700): omit Bearer header on gated-mode REST calls (#701)

In gated mode (basic-auth cookie flow) the dashboard 401s any request that
carries an Authorization: Bearer header alongside the valid session cookie
(verified live 2026-07-23). ApiClient.authInterceptor stamped the WS ticket
as a Bearer header on every REST call, so every non-chat tab (skills, cron,
config, tools, mcp, ...) failed with "token expired" while the WS chat
(?ticket=) kept working.

- Add AuthManager.isGatedMode() (wsAuthParam == "ticket").
- authInterceptor skips the Bearer header in gated mode; loopback/token
  behavior unchanged.
- TokenRefreshAuthenticator bails in gated mode instead of attempting the
  loopback-only SPA token refresh.
- ApiClientTest: add gated/loopback regression tests. 7/7 pass, ktlint clean.

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-07-23 14:46:36 +03:00
1 parent b2a7dabbfc
commit 152b7d5c0d
4 files changed
+85 -6

No files matched your search

@@ -160,6 +160,23 @@ object AuthManager {
serverStore.update { it.copy(wsAuthParam = param) }
}
/**
* True when the active connection profile authenticated against a gated
* dashboard (non-loopback bind → session-cookie auth) instead of loopback
* token mode. Derived from [ServerStoreState.wsAuthParam], which
* [com.m57.hermescontrol.ui.authlogin.AuthLoginViewModel.connect] sets to
* "ticket" on gated (basic-auth) login and "token" on loopback.
*
* Critical: gated dashboards 401 any request carrying an
* `Authorization: Bearer` header — even alongside a valid session cookie
* (verified live 2026-07-23). So REST requests in gated mode MUST rely on
* the session cookie in the shared [CookieManager] jar and MUST NOT stamp a
* Bearer header. This is the fix for the "token expired" error that broke
* every REST tab (skills/cron/config/...) while the WS chat (ticket auth)
* kept working.
*/
fun isGatedMode(): Boolean = serverStore.getLatestState().wsAuthParam == "ticket"
// ── Session Cookie (for gated/dashboard REST API) ────────────────────
/**