Bug Description
When the Hermes Dashboard is bound to 0.0.0.0 (non-loopback) with only basic_auth configured, visiting the root URL redirects to /auth/login?provider=basic which calls start_login() — but the basic auth provider is password-only and raises NotImplementedError, resulting in a 500 Internal Server Error.
This breaks a documented and recommended configuration path: basic auth is listed as one of three bundled auth methods and is the recommended "zero-infra" way to secure the dashboard on a trusted LAN (see Web Dashboard docs).
Steps to Reproduce
- Configure basic auth in config.yaml:
dashboard:
basic_auth:
username: admin
password_hash: "<scrypt hash>"
- Start the dashboard on a non-loopback address:
hermes dashboard --host 0.0.0.0 --port 9119
- Visit
http://<host>:9119/ in a browser
- Observe 500 Internal Server Error
Expected Behavior
Visiting http://<host>:9119/ should redirect to /login (the password form page), where the user can enter credentials and POST to /auth/password-login.
Actual Behavior
The browser receives a 302 Found to /auth/login?provider=basic&next=%2F, which then returns 500 Internal Server Error with:
NotImplementedError: BasicAuthProvider is password-only; there is no OAuth redirect flow. The login page POSTs to /auth/password-login instead.
Affected Component
Dashboard / Auth
Messaging Platform
N/A (CLI only)
Debug Report
version: 0.18.0 [09693cd3] (2026-07-04)
os: Linux 6.18.35 x86_64 (NixOS 26.05)
python: 3.11.15
openai_sdk: 2.24.0
Operating System
NixOS 26.05
Python Version
3.11.15 (Hermes venv) / 3.13.13 (system)
Hermes Version
v0.18.0 (09693cd)
Additional Logs / Traceback
Full traceback from journal:
File "hermes_cli/dashboard_auth/routes.py", line 197, in auth_login
ls = p.start_login(redirect_uri=_redirect_uri(request))
File "plugins/dashboard_auth/basic/__init__.py", line 230, in start_login
raise NotImplementedError(
NotImplementedError: BasicAuthProvider is password-only; there is no OAuth redirect flow. The login page POSTs to /auth/password-login instead.
Root Cause Analysis
The issue is in _auto_sso_response() in hermes_cli/dashboard_auth/middleware.py. When a user visits the dashboard root without a session cookie, the middleware tries to auto-initiate an SSO redirect:
providers = list_session_providers()
if len(providers) != 1:
return None
provider = providers[0]
auth_login = f"{prefix}/auth/login?provider={quote(provider.name, safe='')}"
return RedirectResponse(url=auth_login, status_code=302)
list_session_providers() filters on supports_session=True, which includes the basic auth provider (it has supports_session=True, supports_password=True). With basic auth as the only registered provider, the middleware auto-redirects to /auth/login?provider=basic — the OAuth redirect flow. But basic auth's start_login() is a stub that raises NotImplementedError (it implements complete_password_login instead).
The password-only property already exists on the provider base class (DashboardAuthProvider.supports_password), but _auto_sso_response doesn't check it.
Proposed Fix
Filter out password-only providers from the auto-SSO provider list, since they cannot initiate an OAuth redirect:
# In _auto_sso_response, replace:
providers = list_session_providers()
# With:
providers = [
p for p in list_session_providers()
if not p.supports_password
]
When password-only providers are filtered out and count reaches zero, _auto_sso_response returns None, and the middleware falls through to the ordinary _unauth_response which correctly redirects to /login (the password form page).
Note: _unauth_response already does the right thing — it redirects HTML navigation to /login and returns 401 JSON with login_url for API routes. Only the auto-SSO fast-path has this bug.
Upstream context
The _auto_sso_response function was added in commit f5ecbe1ec (feat(dashboard): auto-initiate portal SSO redirect). It was originally guarded by an _interactive_providers() helper (commit dbe734bef excluded non-session providers), but that helper was later inlined in commit 61f56d27d as list_session_providers() because the supports_session filter was already handled by the registry function. Neither change accounted for password-only (non-OAuth) session providers.
Bug Description
When the Hermes Dashboard is bound to
0.0.0.0(non-loopback) with onlybasic_authconfigured, visiting the root URL redirects to/auth/login?provider=basicwhich callsstart_login()— but the basic auth provider is password-only and raisesNotImplementedError, resulting in a 500 Internal Server Error.This breaks a documented and recommended configuration path: basic auth is listed as one of three bundled auth methods and is the recommended "zero-infra" way to secure the dashboard on a trusted LAN (see Web Dashboard docs).
Steps to Reproduce
http://<host>:9119/in a browserExpected Behavior
Visiting
http://<host>:9119/should redirect to/login(the password form page), where the user can enter credentials and POST to/auth/password-login.Actual Behavior
The browser receives a
302 Foundto/auth/login?provider=basic&next=%2F, which then returns500 Internal Server Errorwith:Affected Component
Dashboard / Auth
Messaging Platform
N/A (CLI only)
Debug Report
Operating System
NixOS 26.05
Python Version
3.11.15 (Hermes venv) / 3.13.13 (system)
Hermes Version
v0.18.0 (09693cd)
Additional Logs / Traceback
Full traceback from journal:
Root Cause Analysis
The issue is in
_auto_sso_response()inhermes_cli/dashboard_auth/middleware.py. When a user visits the dashboard root without a session cookie, the middleware tries to auto-initiate an SSO redirect:list_session_providers()filters onsupports_session=True, which includes the basic auth provider (it hassupports_session=True, supports_password=True). With basic auth as the only registered provider, the middleware auto-redirects to/auth/login?provider=basic— the OAuth redirect flow. But basic auth'sstart_login()is a stub that raisesNotImplementedError(it implementscomplete_password_logininstead).The password-only property already exists on the provider base class (
DashboardAuthProvider.supports_password), but_auto_sso_responsedoesn't check it.Proposed Fix
Filter out password-only providers from the auto-SSO provider list, since they cannot initiate an OAuth redirect:
When password-only providers are filtered out and count reaches zero,
_auto_sso_responsereturnsNone, and the middleware falls through to the ordinary_unauth_responsewhich correctly redirects to/login(the password form page).Note:
_unauth_responsealready does the right thing — it redirects HTML navigation to/loginand returns 401 JSON withlogin_urlfor API routes. Only the auto-SSO fast-path has this bug.Upstream context
The
_auto_sso_responsefunction was added in commitf5ecbe1ec(feat(dashboard): auto-initiate portal SSO redirect). It was originally guarded by an_interactive_providers()helper (commitdbe734befexcluded non-session providers), but that helper was later inlined in commit61f56d27daslist_session_providers()because thesupports_sessionfilter was already handled by the registry function. Neither change accounted for password-only (non-OAuth) session providers.