Skip to content

fix(dashboard-auth): don't auto-redirect to OAuth when sole provider is password-only - #58044

Closed
laserguidedcake wants to merge 1 commit into
NousResearch:mainfrom
laserguidedcake:fix/basic-auth-auto-redirect-crash
Closed

fix(dashboard-auth): don't auto-redirect to OAuth when sole provider is password-only#58044
laserguidedcake wants to merge 1 commit into
NousResearch:mainfrom
laserguidedcake:fix/basic-auth-auto-redirect-crash

Conversation

@laserguidedcake

Copy link
Copy Markdown

Bug Report: Dashboard 500 crash when only password-based auth provider is configured

Summary

When a self-hosted Hermes dashboard is configured with only a password-based authentication provider (e.g. BasicAuthProvider) and bound to a non-loopback host, every unauthenticated request produces a 500 Internal Server Error. The dashboard becomes completely unusable because the auth gate's auto-SSO redirect unconditionally calls start_login() on a provider that does not implement OAuth redirect flow.

Environment

  • Hermes version: 0.18.0 (2026.7.1)
  • Config version: 32
  • Provider: BasicAuthProvider (password-only, no external IDP)
  • Dashboard bind: --host 0.0.0.0 --port 9119 (non-loopback, auth gate engaged)
  • Auth providers: ["basic"] (only one, password-only)

Steps to Reproduce

  1. Configure the dashboard with only BasicAuthProvider:
    dashboard:
      basic_auth:
        username: admin
        password: yourpassword
  2. Start the dashboard on a non-loopback host:
    hermes dashboard --host 0.0.0.0 --port 9119
  3. Open an incognito/private window and navigate to http://your-host:9119/

Expected Behavior

  • The unauthenticated visitor is redirected to /login
  • The login interstitial renders with a username+password form
  • No errors in the service logs

Actual Behavior

  • 500 Internal Server Error on every unauthenticated request
  • Service logs show unhandled NotImplementedError:
    Jul 04 02:55:27 hermes1 hermes[247]:
      NotImplementedError: BasicAuthProvider is password-only;
      there is no OAuth redirect flow. The login page POSTs to
      /auth/password-login instead.
    
  • The dashboard SPA never loads; the login page is inaccessible

Root Cause

Two places in the auth gate call provider.start_login() without checking if the provider actually supports OAuth redirect:

  1. hermes_cli/dashboard_auth/middleware.py:_auto_sso_response()

    • When exactly one supports_session=True provider is registered, the middleware auto-redirects to /auth/login?provider=<name> to silently initiate OAuth.
    • If that sole provider is password-only (supports_password=True, no start_login() implementation), the redirect hits /auth/login, which calls start_login()NotImplementedError.
  2. hermes_cli/dashboard_auth/routes.py:auth_login()

    • The auth_login route calls provider.start_login() inside a try/except ProviderError block.
    • NotImplementedError is not caught, so it bubbles up as an unhandled exception → HTTP 500.

Fix

Patch 1: Skip auto-redirect for password-only providers

File: hermes_cli/dashboard_auth/middleware.py

    provider = providers[0]
+   # Password-only providers have no OAuth redirect flow; skip auto-redirect.
+   if getattr(provider, "supports_password", False):
+       return None
    prefix = prefix_from_request(request)

Patch 2: Defensive catch in auth_login route

File: hermes_cli/dashboard_auth/routes.py

    try:
        ls = p.start_login(redirect_uri=_redirect_uri(request))
+   except NotImplementedError as e:
+       raise HTTPException(
+           status_code=400,
+           detail=str(e),
+       )
    except ProviderError as e:

Regression Test

Added new test: test_password_only_provider_prevents_auto_oauth_redirect_and_gates_login

  • Verifies that / 302-redirects to /login (not /auth/login?provider=...)
  • Verifies that the /login interstitial renders correctly (200 + password form)
  • Verifies that /auth/login?provider=basic-like returns 400 (not 500)

Test result: 34/34 tests in test_dashboard_auth_middleware.py pass, including the new regression test.

Full Branch with Fix + Test

fix/basic-auth-auto-redirect-crash

Branch contains 3 files changed, 62 insertions(+), 1 deletion(-):

  • hermes_cli/dashboard_auth/middleware.py
  • hermes_cli/dashboard_auth/routes.py
  • tests/hermes_cli/test_dashboard_auth_middleware.py

Suggested Commit Message

fix(dashboard-auth): don't auto-redirect to OAuth when sole provider is password-only

When a dashboard is configured with only a password-based auth provider
(e.g., BasicAuthProvider) and binds to a non-loopback host, the
gated-auth middleware's auto-SSO redirect would unconditionally call
provider.start_login(), which raises NotImplementedError.

This produced an unhandled 500 Internal Server Error on every
unauthenticated request, making the dashboard completely unusable in
basic-auth-only self-hosted mode.

The fix has two parts:
1. In _auto_sso_response(): skip auto-redirect when the sole session
   provider has supports_password=True (no OAuth redirect flow).
2. In auth_login(): catch NotImplementedError from start_login() and
   return 400 with the provider's message instead of crashing.

Added regression test.

…is password-only

When a dashboard is configured with only a password-based auth provider
(e.g., BasicAuthProvider) and binds to a non-loopback host, the
gated-auth middleware's auto-SSO redirect would unconditionally call
provider.start_login(), which raises NotImplementedError.

This produced an unhandled 500 Internal Server Error on every
unauthenticated request, making the dashboard completely unusable in
basic-auth-only self-hosted mode.

The fix has two parts:
1. In _auto_sso_response(): skip auto-redirect when the sole session
   provider has supports_password=True (no OAuth redirect flow).
2. In auth_login(): catch NotImplementedError from start_login() and
   return 400 with the provider's message instead of crashing.

Added regression test: test_password_only_provider_prevents_auto_oauth_redirect_and_gates_login

Closes: NousResearch#57410
Reported-by: NullTerminal
@alt-glitch alt-glitch added type/bug Something isn't working comp/dashboard Web dashboard / control panel UI (dashboard/, landing) area/auth Authentication, OAuth, credential pools P2 Medium — degraded but workaround exists duplicate This issue or pull request already exists labels Jul 4, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Duplicate of #54887 — same supports_password guard in the same _auto_sso_response function (the earliest open canonical fix for the sole-password-only auto-SSO HTTP 500). This PR also guards the second start_login() call site in routes.py, so it's slightly broader; maintainers may prefer to fold that delta into #54887. Related: #55130 (anchor issue), #54846 (merged regression source), #57927 (sibling fix PR).

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused reproduction and regression coverage. This has already been implemented on current main by a later dashboard-auth change.

  • Automated hermes-sweeper review verified hermes_cli/dashboard_auth/middleware.py:212, which skips auto-SSO when the sole provider has supports_password.
  • hermes_cli/dashboard_auth/routes.py:195 also handles direct /auth/login requests for password providers by redirecting to /login before start_login() can run.
  • tests/hermes_cli/test_dashboard_auth_password_login.py:201 and :211 cover both paths.
  • The implementation arrived in 3e24b16f566045399012bc1185fe0cdb6e1a1be9 (fix(dashboard): support mobile OAuth login). This also resolves the duplicate context noted in the prior discussion.

@teknium1 teknium1 closed this Jul 15, 2026
@teknium1 teknium1 added the sweeper:implemented-on-main Sweeper: behavior already present on current main label Jul 15, 2026
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) duplicate This issue or pull request already exists P2 Medium — degraded but workaround exists sweeper:implemented-on-main Sweeper: behavior already present on current main type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants