feat(dashboard): auto-initiate portal SSO redirect on unauthenticated load - #54846
Merged
Conversation
… load When the dashboard gateway has no local session cookie, it rendered a click-through /login interstitial — even though the Nous portal's /oauth/authorize auto-approves any current member of the dashboard's org and is a silent 302 when the user already holds a portal session. For the common case (clicking a hosted-agent dashboard link while signed in to the portal) that interstitial click is pure friction. This makes the gate auto-initiate the OAuth redirect on an unauthenticated HTML document load instead of rendering the interstitial, when exactly one interactive provider is registered. A one-shot loop-guard cookie (hermes_sso_attempt, 60s TTL) ensures that a genuinely absent portal session (the portal bounces back still-unauthenticated) falls back to the /login page after exactly one bounce rather than ping-ponging forever. The marker is cleared on a successful callback and whenever the gate falls back to /login. Security: this removes a human CLICK, not a security check. The redirect lands on the existing /auth/login route and runs the unchanged PKCE auth-code flow; token verification, audience checks, redirect-URI match, and org-membership checks are all untouched. /api/* fetches still get the 401 JSON envelope (never a 302 a fetch() would follow opaquely), and with two or more providers the /login chooser still renders. Phase 1 of the cloud-auto-discovery work.
list_session_providers() already filters on supports_session=True, so the new helper re-filtered an already-filtered list. Call it directly at the single auto-SSO call site.
Contributor
🔎 Lint report:
|
This was referenced Jun 29, 2026
This was referenced Jun 29, 2026
1 task
This was referenced Jul 1, 2026
This was referenced Jul 2, 2026
This was referenced Jul 3, 2026
fix(dashboard): basic-auth password-only provider 500s on first load (auto-SSO assumes OAuth)
#57989
Closed
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Clicking a hosted-agent dashboard link while signed in to the Nous portal now lands you in the dashboard with zero clicks — the unauthenticated
/logininterstitial is replaced by an automatic silent OAuth bounce.The portal
/oauth/authorizealready auto-approves any current org member and 302s straight back when the user holds a portal session, so the old click-through interstitial was pure friction for the common case.Changes
middleware.py: new_auto_sso_response()— on an unauthenticated HTML document load with theno_cookiebranch, 302 to the existing/auth/loginOAuth-initiation route instead of rendering/login. Fires only when it's a document load (not/api/*), exactly one interactive provider is registered, and the one-shot loop-guard marker is absent.cookies.py: short-lived (60s)hermes_sso_attemptloop-guard cookie — set on the bounce, read on return, cleared on the/loginfallback and on successful callback. A boolean breadcrumb, no secret.routes.py: clear the guard marker on a successful/auth/callback.X-Forwarded-Prefixand the same-originnext=exactly as the old/loginredirect did.Security
Removes a human click, not a security check. The redirect lands on the unchanged PKCE auth-code flow — token verification,
aud/redirect-URI checks, and org-membership checks are all untouched./api/*fetches still get the 401 JSON envelope (never a 302 afetch()would opaquely follow into the cross-origin OAuth dance). With two-or-more providers the/loginchooser still renders. Provider name isquote()-escaped;next=runs through the existing same-origin open-redirect filter.Validation
/logininterstitial (click)/auth/login/loginonce, marker cleared/api/*unauth fetch/loginchooser/loginchooser (unchanged)99/99 dashboard-auth tests pass (
test_dashboard_auth_401_reauth,_middleware,_prefix). NewTestAutoSsoRedirectcovers the bounce, the one-shot guard, the/api/*carve-out, and the multi-provider chooser.Salvage note
@benbarclay authored the substantive change (commit preserved via rebase). Follow-up on top: dropped the redundant
_interactive_providers()helper —list_session_providers()already filters onsupports_session=True, so the call site now uses it directly.Infographic