Skip to content

fix: dashboard auth crash with NotImplementedError for password-only providers - #721

Open
hashbender wants to merge 1 commit into
mainfrom
mirror/pr-56886
Open

hashbender wants to merge 1 commit into
mainfrom
mirror/pr-56886

Conversation

@hashbender

Copy link
Copy Markdown
Owner

Fix: Dashboard auth crashes with NotImplementedError for password-only providers

Summary

When a dashboard is configured with only a BasicAuthProvider (username/password auth, no OAuth IDP), every unauthenticated request crashes with a 500 NotImplementedError instead of showing the login page. The user can never reach the dashboard.

Root cause: The auto-SSO middleware (_auto_sso_response) and the /auth/login route both assume the sole registered provider supports the OAuth redirect flow (start_login). BasicAuthProvider sets supports_password = True and intentionally raises NotImplementedError from start_login — it has no OAuth flow, only a credential form. The code never checks supports_password before calling start_login, so it blindly invokes a method that raises.

Trace:

  1. User visits dashboard with no session cookie
  2. gated_auth_middleware → no cookie → calls _auto_sso_response()
  3. _auto_sso_response sees exactly 1 provider (basic) → auto-redirects to /auth/login?provider=basic
  4. /auth/login route calls p.start_login(redirect_uri=...)
  5. BasicAuthProvider.start_login() → raises NotImplementedError
  6. Unhandled → 500 Internal Server Error

The fix

Two changes, both checking supports_password before attempting the OAuth path:

1. hermes_cli/dashboard_auth/middleware.py_auto_sso_response

When the sole provider is password-only, return None (fall through to the normal /login redirect). The login page already renders a credential form for supports_password providers.

     provider = providers[0]
+    if getattr(provider, "supports_password", False):
+        # Password-only providers have no OAuth redirect flow — they
+        # need the login page's credential form. Fall through to /login.
+        return None
     prefix = prefix_from_request(request)
     next_param = _safe_next_target(request)

2. hermes_cli/dashboard_auth/routes.pyauth_login

When someone hits /auth/login?provider=basic directly (e.g. bookmark, manual URL), redirect to /login instead of calling start_login and crashing.

     if not getattr(p, "supports_session", True):
         raise HTTPException(
             status_code=404,
             detail=f"Provider does not support interactive login: {provider!r}",
         )

+    if getattr(p, "supports_password", False):
+        # Password-only providers have no OAuth redirect flow.
+        # Redirect to the login page which renders the credential form.
+        from urllib.parse import quote
+        target = "/login"
+        if next:
+            target = f"{target}?next={quote(next, safe='')}"
+        return RedirectResponse(url=target, status_code=302)
+
     try:
         ls = p.start_login(redirect_uri=_redirect_uri(request))
     except ProviderError as e:

Why check supports_password (not provider name)?

Both checks use getattr(p, "supports_password", False) — the same flag the login page already uses to decide between rendering a credential form vs. an OAuth button. This keeps the fix generic: any future password-only provider benefits automatically, with no special-casing by name.

Impact

  • Before: Password-only dashboard is completely unusable — 500 on every page load.
  • After: Password-only dashboard works as designed — login page renders the username/password form, user POSTs to /auth/password-login (the existing, already-working path), gets session cookies.
  • OAuth providers: Completely unaffected. supports_password is False for OAuth providers, so both new checks are no-ops.
  • Mixed (OAuth + password): Unaffected. _auto_sso_response already returns None when len(providers) != 1.

Testing

Reproduce the original crash with a minimal BasicAuthProvider-only setup:

# Start dashboard with only basic auth configured
HERMES_DASHBOARD_BASIC_AUTH_USERNAME=admin \
HERMES_DASHBOARD_BASIC_AUTH_PASSWORD=secret \
python -m hermes_cli dashboard --host 0.0.0.0 --port 9119 --no-open

Before fix: curl http://localhost:9119/ → 500 NotImplementedError: BasicAuthProvider is password-only

After fix: curl http://localhost:9119/ → 302 → /login (renders credential form)

# Login via the password endpoint (existing, already working)
curl -X POST http://localhost:9119/auth/password-login \
  -H 'Content-Type: application/json' \
  -d '{"provider":"basic","username":"admin","password":"secret"}'
# → {"ok": true, "next": "/"} + session cookies

Backwards compatibility

No config changes, no API changes, no migration. Existing OAuth-only and mixed deployments are completely unaffected — the new supports_password checks are no-ops for any provider where supports_password is False (the default).


Mirror-of: NousResearch#56886
NousResearch#56886

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant