fix(dashboard): route single password-only provider to /login, not OAuth (500 on basic-auth login) - #58802
Conversation
v0.18.0 hardening requires an auth provider to bind the dashboard non-loopback.
With a single BasicAuthProvider (password-only) configured, two paths bounce the
browser to the OAuth-initiation route /auth/login?provider=basic, which calls
start_login() — NotImplementedError for a password provider — returning a 500:
1. _auto_sso_response() auto-redirects whenever exactly ONE session provider
is registered, guarding on provider COUNT but not capability.
2. GET /auth/login itself calls start_login() unconditionally.
Guard both on supports_password and fall through / redirect to the server-
rendered /login form (which POSTs to /auth/password-login). Upstream only
exercises single-provider setups with OAuth, so this path was never hit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Duplicate of #56886 — both PRs apply the identical both-site |
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: Approved
Simple one-line fix replacing len() truthiness with idiomatic Python. Clean and focused.
Reviewed by Hermes Agent
|
Verified this independently against a clean Wanted to raise my own PR but per the repo's contribution clause ("If an open PR already addresses it, consider reviewing or improving that one instead of opening a competing duplicate"), checked for a previous fix first and found this one (plus several other independent duplicates) already open, so reviewing here instead. One thing I'd suggest addressing before merge: this has no test coverage. Per CONTRIBUTING.md, bug-fix PRs are expected to include a regression test. I wrote a small standalone-stub-based test suite while investigating the same bug (not coupled to the concrete Also worth noting your |
|
Thanks for the focused diagnosis and for covering both the auto-SSO and direct-login paths. This is an automated hermes-sweeper review; the requested behavior is already on current
|
Problem
With a single password-only auth provider (
BasicAuthProvider) configured, the dashboard returns HTTP 500 on any unauthenticated navigation.The June-2026 hardening requires an auth provider to bind the dashboard to a non-loopback host. Configuring
dashboard.basic_authsatisfies the bind gate, but logging in fails: two code paths bounce the browser to the OAuth-initiation routeGET /auth/login?provider=<name>, which callsprovider.start_login()— andBasicAuthProvider.start_login()raisesNotImplementedError("password-only; there is no OAuth redirect flow").Root cause
Two spots dispatch to the OAuth flow without checking whether the provider actually supports it:
hermes_cli/dashboard_auth/middleware.py::_auto_sso_response— auto-redirects to/auth/login?provider=<name>whenever exactly one session provider is registered. It guards on provider count, not capability, so a lone password provider is treated like a lone OAuth IDP.hermes_cli/dashboard_auth/routes.py::auth_login— callsstart_login()unconditionally after thesupports_sessioncheck.The correct server-rendered form at
GET /login(which POSTs to/auth/password-login) works fine — the gate just never routes password providers there. This only manifests with a single-provider password setup; single-provider OAuth (the common self-hosted path) is unaffected, which is presumably why it went unnoticed.Fix
Guard both paths on
supports_password:_auto_sso_responsereturnsNone(falls through to the/loginchooser) when the single provider is password-only.auth_loginredirects to/login?next=...instead of callingstart_login()for password-only providers (handles direct/bookmarked hits gracefully).No behavior change for OAuth providers.
Testing
Single
BasicAuthProvider, dashboard bound to0.0.0.0:GET /(unauth)/auth/login?provider=basic→ 500/loginGET /auth/login?provider=basic/loginGET /loginPOST /auth/password-login(valid){"ok":true}{"ok":true}POST /auth/password-login(wrong pw)🤖 Generated with Claude Code