hermes-agent/hermes_cli/dashboard_auth
Ben 439f53cab8 fix(desktop): gate OAuth remote connect on AT-or-RT, not access token alone
The desktop OAuth remote-gateway path gated connectivity on
hasOauthSessionCookie(), which checks only the access-token cookie
(hermes_session_at, ~15 min TTL). The moment that cookie's Max-Age
lapsed, Electron's cookie jar dropped it and both resolveRemoteBackend()
and sanitizeDesktopConnectionConfig() reported "not signed in" — forcing
a full IDP re-login every ~15 min — even though a valid 24h refresh-token
cookie (hermes_session_rt) was sitting in the same jar.

The desktop OAuth code (2026-06-04) was written against the obsolete
"contract v1 issues no refresh token" model, two days after #37247
re-introduced server-side transparent refresh: Portal now issues a 24h
rotating, reuse-detected refresh token, and the gateway middleware
(_attempt_refresh) rotates a fresh AT from the RT on the next
authenticated request. So an expired-AT/live-RT session is fully
connectable — the desktop just never let the request through.

Fix:
- connection-config.cjs: add RT_COOKIE_VARIANTS + cookiesHaveLiveSession()
  (true when EITHER a live AT or RT cookie is present). Keep
  cookiesHaveSession() AT-only for callers that need that specific signal.
- main.cjs: add hasLiveOauthSession(); resolveRemoteBackend()'s oauth
  branch now early-outs only when NEITHER cookie is present, otherwise
  uses the ws-ticket mint as the authoritative liveness probe (that POST
  carries the RT cookie and triggers the server-side AT rotation). A real
  401 still surfaces as needsOauthLogin. Settings indicator + oauth-logout
  report against the same AT-or-RT notion.
- Remove the stale "contract v1 / NO refresh token" docstrings in
  cookies.py and the verify_session comments in the Nous provider that
  contradicted #37247.

Tests: +57 lines in connection-config.test.cjs covering the RT-only
"still connectable" case. node --test: 32/32. dashboard-auth +
nous-provider Python suites: 223/223.

Note: server-side files (hermes_cli/dashboard_auth/, plugins/dashboard_auth/)
are comment/docstring-only here, but this touches outside apps/desktop/ so
it needs Teknium review.
2026-06-04 22:18:46 -07:00
..
__init__.py feat(dashboard-auth): add pluggable password (non-redirect) login 2026-06-04 01:02:25 -07:00
audit.py feat(dashboard-auth): single-use WS tickets + POST /api/auth/ws-ticket 2026-05-27 02:12:27 -07:00
base.py feat(dashboard-auth): add pluggable password (non-redirect) login 2026-06-04 01:02:25 -07:00
cookies.py fix(desktop): gate OAuth remote connect on AT-or-RT, not access token alone 2026-06-04 22:18:46 -07:00
login_page.py feat(dashboard-auth): add pluggable password (non-redirect) login 2026-06-04 01:02:25 -07:00
middleware.py fix(dashboard-auth): don't abort verify chain on one provider's ProviderError 2026-06-04 03:23:45 -07:00
prefix.py feat(dashboard-auth): HERMES_DASHBOARD_PUBLIC_URL / dashboard.public_url override 2026-05-27 02:12:27 -07:00
public_paths.py fix(dashboard-auth): share /api/* public allowlist between legacy and OAuth gates 2026-05-29 12:17:12 +10:00
registry.py feat(dashboard-auth): define DashboardAuthProvider ABC + Session dataclass 2026-05-27 02:12:27 -07:00
routes.py feat(dashboard-auth): add pluggable password (non-redirect) login 2026-06-04 01:02:25 -07:00
ws_tickets.py test(dashboard): direct unit coverage for internal WS credential + docstring fix 2026-06-02 23:43:27 -07:00