feat(dashboard): auto-initiate portal SSO redirect on unauthenticated load - #54713
Closed
benbarclay wants to merge 1 commit into
Closed
feat(dashboard): auto-initiate portal SSO redirect on unauthenticated load#54713benbarclay wants to merge 1 commit into
benbarclay wants to merge 1 commit into
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.
Contributor
🔎 Lint report:
|
benbarclay
marked this pull request as ready for review
June 29, 2026 08:11
tonydwb
reviewed
Jun 29, 2026
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: LGTM
Auto-SSO redirect for dashboard: when exactly one interactive provider is registered, unauthenticated HTML loads auto-initiate the portal OAuth redirect instead of showing the /login interstitial. A one-shot cookie guard prevents infinite redirect loops when the portal has no session.
Looks Good
- Loop guard (SSO_ATTEMPT_COOKIE) with 60s TTL prevents ping-pong
- API paths never auto-redirect (correct — fetch() would follow opaquely)
- Multiple providers fall back to /login chooser
- Marker cleared on successful callback and fallback
- Comprehensive tests: auto-redirect, guard trip, loop safety, API exclusion
Reviewed by Hermes Agent
Contributor
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.
What
When the Hermes Agent dashboard gateway has no local session cookie, it rendered a click-through /login interstitial — even though the Nous portal's
/oauth/authorizeauto-approves any current member of the dashboard's org (no consent UI) and is a silent 302 when the user already holds a portal session.So for the common case — clicking a hosted-agent dashboard link while signed in to the portal — that interstitial click is pure friction. This change makes the gate auto-initiate the OAuth redirect on an unauthenticated HTML document load instead.
How
gated_auth_middleware, theno_cookieHTML branch now tries_auto_sso_response()before falling back to the/logininterstitial./api/*fetch), exactly one interactive provider is registered, and the one-shot loop-guard marker is absent.hermes_sso_attemptcookie (60s) is set on the bounce. If the user returns from the portal still unauthenticated (no portal session), the marker is present → we fall back to/loginexactly once and clear the marker. No infinite ping-pong./auth/callback.X-Forwarded-Prefixand the same-originnext=exactly as the old/loginredirect did.Security
This removes a human click, not a security check. The redirect lands on the existing
/auth/loginroute and runs the unchanged PKCE auth-code flow — token verification,audchecks, redirect-URI match, and org-membership checks are all untouched./api/*fetches still get the 401 JSON envelope (never a 302 afetch()would follow opaquely into the cross-origin OAuth dance). With two or more providers, the/loginchooser still renders (we can't pick for the user).Tests
TestAutoSsoRedirect(new): unauth HTML load auto-redirects + arms the guard; second load with guard present falls back to/loginand clears the marker (the loop-safety test);/api/*never auto-redirects; multiple providers render the chooser.test_dashboard_auth_401_reauth.py,test_dashboard_auth_middleware.py, andtest_dashboard_auth_prefix.pyto reflect the new/auth/logintarget (the prefix-carrying andnext=invariants are preserved — only the target path moved).test_dashboard_auth_password_login.py(3 tests fail only in a combined run, pass in isolation) — confirmed identical on cleanmainwith this branch's changes stashed, so it is unrelated to this PR.Phase 1 of the cloud-auto-discovery work.