Skip to content

fix(dashboard-auth): bind basic-auth sessions to a persisted epoch (RAH-01) - #77207

Open
JoaoMarcos44 wants to merge 1 commit into
NousResearch:mainfrom
JoaoMarcos44:fix/rah01-basic-auth-session-epoch
Open

fix(dashboard-auth): bind basic-auth sessions to a persisted epoch (RAH-01)#77207
JoaoMarcos44 wants to merge 1 commit into
NousResearch:mainfrom
JoaoMarcos44:fix/rah01-basic-auth-session-epoch

Conversation

@JoaoMarcos44

@JoaoMarcos44 JoaoMarcos44 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #77186.

BasicAuthProvider (plugins/dashboard_auth/basic/__init__.py) mints stateless HMAC-signed access/refresh tokens with no server-side session record. verify_session() only checked the signature and expiry — nothing tied a token to "the credential that was active when it was minted". Two consequences, both reproduced deterministically before this fix:

  1. revoke_session() was a documented no-op. A copied/leaked access token stayed valid for its full TTL (default 12h access / 30d refresh) after logout.
  2. Rotating the password while keeping an explicit, restart-stable secret did not invalidate previously issued tokens.

Fix

Every token now carries an epoch claim, checked at verification time against the provider's current epoch. The epoch is persisted (atomic JSON write, dashboard_auth_basic_session_epoch.json under HERMES_HOME) and keyed to a fingerprint of the credential source material — the plaintext/precomputed hash as configured, not the salted-per-restart scrypt output, so a plain restart with an unchanged password does not spuriously log everyone out. revoke_session() bumps the epoch (this provider has exactly one identity, so "logout" is necessarily "logout everywhere" for it). register() detects a fingerprint change across a restart and bumps the epoch too, closing the rotation gap.

Direct construction (tests, or any future caller) without a credential_fingerprint keeps the previous in-memory-only behavior — fully backward compatible.

%%{init: {'theme': 'dark', 'themeVariables': { 'primaryColor': '#00f0ff', 'mainBkg': '#0a0a16', 'primaryTextColor': '#ffffff', 'primaryBorderColor': '#ff007f', 'lineColor': '#00f0ff'}}}%%
graph TD
    A[🔒 revoke_session] -->|bump epoch| B[⚡ Persisted Epoch Store]
    C[🔁 password rotation] -->|fingerprint changed| B
    B -->|epoch claim on new tokens| D[🚀 Mint Session]
    E[🎫 Incoming Token] -->|epoch claim| F{Token Verify}
    B -->|current epoch| F
    F -->|mismatch| G[🚫 Rejected]
    F -->|match| H[✅ Session Valid]
Loading

Infographic :

infographic

Test plan

  • New tests: test_revoke_session_invalidates_prior_access_token, test_password_rotation_invalidates_prior_session_across_restart, test_same_password_across_restart_keeps_session_valid
  • Each new test confirmed to fail against the pre-fix code (revoke: token still valid; rotation: TypeError on missing kwarg pre-refactor equivalent)
  • Full suite: tests/plugins/dashboard_auth/test_basic_provider.py — 22 passed
  • python -m py_compile plugins/dashboard_auth/basic/__init__.py

…AH-01)

BasicAuthProvider's tokens are stateless HMAC blobs with no server-side
session record, so revoke_session() was a documented no-op and rotating
the password (with an explicit, restart-stable secret) never invalidated
tokens already issued — verify_session() only checked the signature and
expiry, never anything derived from the current credential.

Give every token an "epoch" claim checked at verification time. The epoch
is persisted (atomic JSON write) and keyed to a fingerprint of the
credential source material (not the salted hash, which changes every
restart even when the password doesn't): register() detects a fingerprint
change and bumps the epoch, so both an explicit revoke_session() and a
credential rotation across a restart invalidate every previously issued
token. Same fingerprint across a restart leaves the epoch untouched,
preserving the documented explicit-secret multi-worker/restart-survival
contract.

Direct construction (tests, or any future caller) without a
credential_fingerprint keeps the previous in-memory-only behavior.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/dashboard Web dashboard / control panel UI (dashboard/, landing) comp/plugins Plugin system and bundled plugins area/auth Authentication, OAuth, credential pools sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Aug 3, 2026

@GottZ GottZ left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was generated by AI during triage.

Summary

Two PRs address the session-revocation and password-rotation defect: #77174 includes the persisted session-epoch fix within a broader five-finding bundle, while #77207 isolates the same RAH-01 implementation and regression coverage for focused review.

Related pull requests

  • #77174 [closed] best fix — (+519/-109) — superseded by #77207 for RAH-01: the bundled diff fixes the root cause by adding persisted credential-bound epochs to access and refresh tokens, invalidating old tokens on revocation or credential rotation. Although #77174 is the recorded best fix for #77172 and #77186, it remains relevant as the source implementation; a contributor closed it in favor of issue-specific PRs, and its RAH-01 changes are carried into #77207.
  • #77207 fixes — (+201/-5) — focused salvage path: the diff extracts #77174's RAH-01 fix into the dashboard-auth provider, including epoch validation, atomic epoch persistence, credential-fingerprint rotation detection, and tests for revocation, password rotation, and unchanged-password restarts.

Duplicates

#77174 and #77207 contain the same substantive RAH-01 session-epoch change; #77207 is the focused successor, while #77174 also contains unrelated fixes for the other split findings.

Suggested consolidation

Keep #77207 open with a salvage path focused strictly on its persisted session-epoch implementation and the three RAH-01 regression cases. Keep #77174 closed as superseded for this issue: despite its recorded best-fix verdict for #77172 and #77186, the diff shows that the relevant RAH-01 implementation was preserved in #77207, consistent with the contributor's explicit decision to split the bundled PR into issue-specific changes.

Complex graph

flowchart LR
    classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
    classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
    classDef best stroke-width:3px,stroke:#b45309
    classDef target stroke-width:3px,stroke:#4338ca
    I77186(["issue #77186 (open)"])
    P77207["PR #77207 (open)"]
    P77207 -->|fixes| I77186
    class I77186 open
    class P77207 open
    class P77207 target
    click I77186 "https://github.com/NousResearch/hermes-agent/issues/77186"
    click P77207 "https://github.com/NousResearch/hermes-agent/pull/77207"
Loading

Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label).

Cross-PR triage: Reviewed 2 pull requests and 2 issues in this complex. Each diff was read against this issue; Assessment working set: 53 kB of PR diffs, 19 kB of issue/PR text, <1 kB of discussion (2 comments), 4 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/dashboard Web dashboard / control panel UI (dashboard/, landing) comp/plugins Plugin system and bundled plugins P2 Medium — degraded but workaround exists sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(dashboard-auth): basic-auth session survives revoke_session() and password rotation (RAH-01)

3 participants