The dashboard auth gate was OAuth-only: a DashboardAuthProvider could
authenticate only via a redirect to an IDP (start_login -> /auth/callback
-> complete_login). There was no first-class path for username/password
auth, so self-hosters who just want a password on their dashboard had no
clean option short of an external OAuth IDP.
Extend the provider framework with a parallel, non-redirect front door
that converges on the same Session + cookie + refresh machinery:
- base.py: add the optional supports_password flag and
complete_password_login(username, password) -> Session (default
raises NotImplementedError so an OAuth-only provider that forgets the
flag fails loudly). Add InvalidCredentialsError. OAuth providers are
unaffected (flag defaults False; the method is never called).
- routes.py: add POST /auth/password-login, mirroring the cookie-minting
tail of /auth/callback but skipping PKCE/state/code. Returns JSON
{ok, next} (the form POSTs via fetch). Generic 401 for both unknown
user and wrong password (no enumeration oracle); 404 hides whether a
provider exists or supports passwords; per-IP sliding-window rate
limit (10/min -> 429). /api/auth/providers now reports
supports_password so the login page can branch.
- middleware.py: allowlist /auth/password-login (a bootstrap route).
verify/refresh/revoke/ws-tickets/logout need zero changes — a password
session is just a Session with provider-minted opaque tokens.
- login_page.py: render a credential form (instead of a redirect button)
for supports_password providers, wired by a small inline script that
POSTs to /auth/password-login and navigates on success. OAuth-only
pages stay script-free.
43 lines
1.1 KiB
Python
43 lines
1.1 KiB
Python
"""Dashboard authentication provider framework.
|
|
|
|
The dashboard auth gate engages only when the dashboard binds to a
|
|
non-loopback host without ``--insecure``. In that mode, every request must
|
|
carry a verified session from one of the registered ``DashboardAuthProvider``
|
|
plugins.
|
|
|
|
The Nous provider lives in ``plugins/dashboard-auth-nous/`` and is the
|
|
default. Third parties register their own providers via the plugin hook
|
|
``ctx.register_dashboard_auth_provider``.
|
|
"""
|
|
from hermes_cli.dashboard_auth.base import (
|
|
DashboardAuthProvider,
|
|
Session,
|
|
LoginStart,
|
|
InvalidCodeError,
|
|
InvalidCredentialsError,
|
|
ProviderError,
|
|
RefreshExpiredError,
|
|
assert_protocol_compliance,
|
|
)
|
|
from hermes_cli.dashboard_auth.registry import (
|
|
register_provider,
|
|
get_provider,
|
|
list_providers,
|
|
clear_providers,
|
|
)
|
|
|
|
__all__ = [
|
|
"DashboardAuthProvider",
|
|
"Session",
|
|
"LoginStart",
|
|
"InvalidCodeError",
|
|
"InvalidCredentialsError",
|
|
"ProviderError",
|
|
"RefreshExpiredError",
|
|
"assert_protocol_compliance",
|
|
"register_provider",
|
|
"get_provider",
|
|
"list_providers",
|
|
"clear_providers",
|
|
]
|