Bug Description
Summary
With the self_hosted OIDC provider, logging out of the dashboard does not end the session at the identity provider. The user is silently signed back in on the next page view, and switching accounts is impossible without manually clearing the IdP session.
revoke_session() calls the RFC 7009 revocation endpoint with the refresh token. It never calls the provider's end_session_endpoint (RP-initiated logout), even though the discovery document advertises one.
With Keycloak — and any IdP that keeps one SSO session per browser with a client session per application — revoking the refresh token removes only this application's client session. The SSO session survives as long as any other application is attached to it. The dashboard's silent SSO attempt then immediately re-authenticates the same user.
Why this is easy to miss
The bug is intermittent in a way that misleads: if the dashboard happens to be the only application attached to the SSO session, revocation removes the last client session, the IdP drops the SSO session as well, and logout appears to work correctly. It only fails once a second application in the same realm is also signed in.
Verified against the IdP in our deployment — one SSO session, two client sessions:
GET /admin/realms/<realm>/users/<uid>/sessions
→ 1 session, clients: ['<other-app-client-id>', '<hermes-client-id>']
A further consequence of the same root cause: two Hermes containers sharing one /opt/data and served under two different hostnames (hermes serve and hermes dashboard) share the IdP session. Signing in on one changes the identity shown by the other.
Relevant code
plugins/dashboard_auth/self_hosted/__init__.py, revoke_session():
endpoint = str(disco.get("revocation_endpoint") or "").strip()
if not endpoint:
return None
...
httpx.post(endpoint, data=data, headers=headers, timeout=_TOKEN_ENDPOINT_TIMEOUT_SEC)
hermes_cli/dashboard_auth/routes.py, auth_logout() — revokes and clears cookies; no IdP redirect.
Suggested fix
After the local cleanup, redirect the browser to the provider's end_session_endpoint when discovery advertises one:
{end_session_endpoint}?id_token_hint=<id_token>&post_logout_redirect_uri=<dashboard public url>
client_id works as an alternative to id_token_hint where no ID token is retained. Token revocation is worth keeping — the two are complementary: revocation invalidates the tokens, RP-initiated logout ends the session.
Two details from deploying this:
- The public URL should come from the existing
HERMES_DASHBOARD_PUBLIC_URL / dashboard.public_url resolution, so the post-logout redirect is correct behind a reverse proxy.
- IdPs validate
post_logout_redirect_uri against a separate allow-list (Keycloak: the client attribute post.logout.redirect.uris), not against the redirect URIs. Worth a line in the docs — it is an easy misconfiguration.
Secondary observation (lower severity)
revoke_session() is best-effort and swallows every failure at debug level, while the cookies are cleared regardless. A failed revocation is therefore indistinguishable from a successful logout for the user. A warning-level log would have shortened our diagnosis considerably.
Steps to Reproduce
- Configure
dashboard.oauth.self_hosted against an IdP that maintains an SSO session (Keycloak 26.1 here) and register a second OIDC client in the same realm for any other application.
- Sign in to the other application in the same browser.
- Sign in to the Hermes dashboard as the same user.
- Click logout in the dashboard.
- Load any dashboard page again.
Expected: the login screen; a different account can be chosen.
Actual: signed in again as the same user, without a prompt. Clearing browser cookies does not help — the IdP session is still valid, so the next silent attempt succeeds again.
Expected Behavior
- Logout does not maintain SSO session and therefore does not recreate the access
Actual Behavior
- Logout maintains SSO session and therefore recreated the hermes dashboard session automatically
Affected Component
Gateway (Telegram/Discord/Slack/WhatsApp)
Messaging Platform (if gateway-related)
No response
Debug Report
Operating System
Ubuntu 24.04
Python Version
No response
Hermes Version
No response
Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
No response
Proposed Fix (optional)
No response
Are you willing to submit a PR for this?
Bug Description
Summary
With the
self_hostedOIDC provider, logging out of the dashboard does not end the session at the identity provider. The user is silently signed back in on the next page view, and switching accounts is impossible without manually clearing the IdP session.revoke_session()calls the RFC 7009 revocation endpoint with the refresh token. It never calls the provider'send_session_endpoint(RP-initiated logout), even though the discovery document advertises one.With Keycloak — and any IdP that keeps one SSO session per browser with a client session per application — revoking the refresh token removes only this application's client session. The SSO session survives as long as any other application is attached to it. The dashboard's silent SSO attempt then immediately re-authenticates the same user.
Why this is easy to miss
The bug is intermittent in a way that misleads: if the dashboard happens to be the only application attached to the SSO session, revocation removes the last client session, the IdP drops the SSO session as well, and logout appears to work correctly. It only fails once a second application in the same realm is also signed in.
Verified against the IdP in our deployment — one SSO session, two client sessions:
A further consequence of the same root cause: two Hermes containers sharing one
/opt/dataand served under two different hostnames (hermes serveandhermes dashboard) share the IdP session. Signing in on one changes the identity shown by the other.Relevant code
plugins/dashboard_auth/self_hosted/__init__.py,revoke_session():hermes_cli/dashboard_auth/routes.py,auth_logout()— revokes and clears cookies; no IdP redirect.Suggested fix
After the local cleanup, redirect the browser to the provider's
end_session_endpointwhen discovery advertises one:client_idworks as an alternative toid_token_hintwhere no ID token is retained. Token revocation is worth keeping — the two are complementary: revocation invalidates the tokens, RP-initiated logout ends the session.Two details from deploying this:
HERMES_DASHBOARD_PUBLIC_URL/dashboard.public_urlresolution, so the post-logout redirect is correct behind a reverse proxy.post_logout_redirect_uriagainst a separate allow-list (Keycloak: the client attributepost.logout.redirect.uris), not against the redirect URIs. Worth a line in the docs — it is an easy misconfiguration.Secondary observation (lower severity)
revoke_session()is best-effort and swallows every failure at debug level, while the cookies are cleared regardless. A failed revocation is therefore indistinguishable from a successful logout for the user. A warning-level log would have shortened our diagnosis considerably.Steps to Reproduce
dashboard.oauth.self_hostedagainst an IdP that maintains an SSO session (Keycloak 26.1 here) and register a second OIDC client in the same realm for any other application.Expected: the login screen; a different account can be chosen.
Actual: signed in again as the same user, without a prompt. Clearing browser cookies does not help — the IdP session is still valid, so the next silent attempt succeeds again.
Expected Behavior
Actual Behavior
Affected Component
Gateway (Telegram/Discord/Slack/WhatsApp)
Messaging Platform (if gateway-related)
No response
Debug Report
Operating System
Ubuntu 24.04
Python Version
No response
Hermes Version
No response
Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
No response
Proposed Fix (optional)
No response
Are you willing to submit a PR for this?