2f44998353
NousDashboardAuthProvider._verify_jwt (and the identical hunk in the
self-hosted OIDC provider) folded EVERY PyJWKClient failure into
ProviderError, which the gate translates to HTTP 503
{"detail":"Auth provider 'nous' unreachable"}. That branch fires for
jwt.DecodeError('Not enough segments') — i.e. the bearer is not a JWT at all
(an opaque peer key, a legacy token, garbage) — and for PyJWKSetError (JWKS
fetched fine, foreign kid). Neither involves reaching Portal, which is why
the hosted sjc agents in #94558 returned a fast, well-formed 503 that
survived token re-mint and instance restart while Portal was healthy.
Add one shared classifier, hermes_cli.dashboard_auth.classify_jwks_lookup_error:
only PyJWKClientConnectionError (transport) and an unexpected bare
PyJWKClientError stay ProviderError; DecodeError / PyJWKSetError /
InvalidTokenError become InvalidCodeError so verify_session() returns None
and the middleware proceeds to the next provider / refresh / 401 exactly as
the protocol documents. Both providers now use it.
Live repro (real NousDashboardAuthProvider against a local reachable JWKS
server; and the real gated web_server app): before — opaque bearer ->
ProviderError "JWKS lookup failed: DecodeError('Not enough segments')" ->
503 unreachable; after — verify_session() -> None, gated GET /api/auth/me
with the opaque bearer -> 401; a real JWT against an unreachable JWKS still
-> ProviderError (503).
This does not add /api/v1/message to the public-path allowlist (#94579):
that route has no verifier in this repo, so bypassing the gate would leave a
state-changing ingress fail-open. The correct fix is classification, which
also covers every other opaque-bearer surface.
Refs #94558
51 lines
1.3 KiB
Python
51 lines
1.3 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,
|
|
TokenPrincipal,
|
|
LoginStart,
|
|
InvalidCodeError,
|
|
InvalidCredentialsError,
|
|
ProviderError,
|
|
RefreshExpiredError,
|
|
assert_protocol_compliance,
|
|
classify_jwks_lookup_error,
|
|
)
|
|
from hermes_cli.dashboard_auth.registry import (
|
|
register_provider,
|
|
get_provider,
|
|
list_providers,
|
|
list_token_providers,
|
|
list_session_providers,
|
|
clear_providers,
|
|
)
|
|
|
|
__all__ = [
|
|
"DashboardAuthProvider",
|
|
"Session",
|
|
"TokenPrincipal",
|
|
"LoginStart",
|
|
"InvalidCodeError",
|
|
"InvalidCredentialsError",
|
|
"ProviderError",
|
|
"RefreshExpiredError",
|
|
"assert_protocol_compliance",
|
|
"classify_jwks_lookup_error",
|
|
"register_provider",
|
|
"get_provider",
|
|
"list_providers",
|
|
"list_token_providers",
|
|
"list_session_providers",
|
|
"clear_providers",
|
|
]
|