Skip to content

Add SSO support: two-round enforcement and pc target re-auth#86

Merged
austin-denoble merged 15 commits intomainfrom
adenoble/sso-support
Apr 20, 2026
Merged

Add SSO support: two-round enforcement and pc target re-auth#86
austin-denoble merged 15 commits intomainfrom
adenoble/sso-support

Conversation

@austin-denoble
Copy link
Copy Markdown
Collaborator

@austin-denoble austin-denoble commented Apr 13, 2026

Problem

The CLI had no support for organizations with SSO enforced. Users in SSO-required orgs would land on the generic Auth0 login page instead of their identity provider, and authentication would either fail or succeed without proper SSO routing.

Solution

Core mechanism

Auth0 SSO routing requires a connection=<name> parameter in the authorization URL. This PR adds a best-effort lookup of the connection name via the private dashboard organizations API (authenticated with the user's existing token), then passes it through to the PKCE auth URL. The lookup is non-fatal — if it fails or the org has no SSO configured, the flow falls back transparently to the standard login page.

Two-round transparent enforcement

SSO enforcement is detected after round-1 authentication (once the token is available to query the dashboard API). When SSO is required, the CLI automatically starts a second login round with the connection= parameter set, routing the browser to the org's IdP. This happens transparently in both flows:

  • Interactive (pc login without --json): After code exchange, if SSO is enforced, the outer callback server's stdin-watching goroutine is cancelled before recursing into a fresh interactive round — preventing a goroutine race where the old goroutine could open the wrong URL on the next Enter keypress.
  • Agentic / JSON (pc login --json): finishAuthWithSSO is called when a session completes. If SSO is enforced, it cleans up the completed session eagerly (so findResumableSession won't resurrect it), logs out, and starts a new daemon-backed session — emitting a new pending JSON with the SSO URL for the agent to present.

SessionState carries a SSOConnection field so round-2 sessions skip the enforcement check and don't loop.

"Already logged in" path

Run() now checks SSO enforcement before showing "already logged in". If SSO is required for the current org, the existing token is cleared and the SSO round starts immediately — covering the case where a user authenticates and SSO is later enabled for their org.

Lazy auth completion (EnsureAuthenticated)

When a background daemon completes round-1 auth and the next CLI command triggers lazy completion, EnsureAuthenticated now checks SSO enforcement before handing off. If SSO is required, it leaves the session intact and directs the user to pc login to complete the SSO round, rather than silently proceeding with a non-SSO token.

pc target re-auth

Both re-authentication sites in pc target now resolve the SSO connection name before calling oauth.Logout(), so the lookup can use the still-valid token.

Correctness fixes bundled in this PR

  • Session org-mismatch guard handles sess.OrgId == nil (previously would skip the mismatch check entirely)
  • Nil pointer dereference in error message when sess.OrgId is nil (now defaults to "unknown")
  • Claims parse failure in the "already logged in" path is logged at debug level rather than silently swallowed
  • serverCtx uses context.Background() as its base so the root command's 60s default timeout doesn't cut off a user mid-authentication
  • Code exchange and post-callback SSO lookup use a fresh 30s context from context.Background() for the same reason

Type of Change

  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)

Test Plan

  • just test-unit passes (includes new unit tests for GetAuthURL with connection= param and fetchSSOConnectionFromURL across org-found, org-not-found, non-2xx, empty connection name, and multi-org cases)
  • Interactive SSO flow: pc login against an SSO-enforced org triggers round 2 automatically after round 1 completes
  • Agentic flow: pc login --json polling loop receives pending → (SSO) pendingauthenticated
  • pc target <sso-org> re-authenticates via SSO when switching orgs
  • Non-SSO orgs unaffected: standard login flow unchanged

Note

Medium Risk
Touches the CLI OAuth login flow (interactive + daemon/JSON) and session resumption logic, which can affect all authentication paths. SSO lookup is best-effort and should fall back safely, but incorrect org/session handling could block logins.

Overview
Adds SSO-aware OAuth login by looking up an org's Auth0 connection via the dashboard API and injecting it into the authorization URL.

Implements two-round SSO enforcement: after an initial login, the CLI detects enforce_sso_authentication and automatically restarts login using the resolved connection (both interactive and --json daemon flows), tracking round-2 state via SessionState.SSOConnection.

Updates pc target org switching to resolve the SSO connection before oauth.Logout() and pass it into login.GetAndSetAccessToken, and includes a few correctness fixes around session/org mismatch handling and timeouts during browser auth.

Reviewed by Cursor Bugbot for commit f6e9247. Bugbot is set up for automated code reviews on this repo. Configure here.

  When authenticating into an organization that has SSO enforced, the CLI
  now passes the Auth0 connection name as a connection= parameter to the
  authorization endpoint, routing the browser directly to the org's
  identity provider rather than the generic login page.

  Both pc login and pc auth login accept a new --org flag to scope the
  login to a specific organization. pc target already passes the org ID
  implicitly. In all cases the SSO connection is resolved by calling the
  dashboard organizations API with the user's existing token; the lookup
  is best-effort and non-fatal — if it fails the flow falls back to the
  standard login page transparently.
Comment thread internal/pkg/utils/login/login.go Outdated
Comment thread internal/pkg/utils/login/login.go Outdated
Comment thread internal/pkg/utils/login/login.go Outdated
Comment thread internal/pkg/utils/login/login.go
Comment thread internal/pkg/utils/login/login.go
Comment thread internal/pkg/utils/login/login.go
Comment thread internal/pkg/utils/login/login.go
…rly handles SSO, surfacing an error when an extra browser verification step is needed
Comment thread internal/pkg/utils/login/login.go
Comment thread internal/pkg/utils/login/login.go
…e login path, gracefull resume. prevent swallowing claims parsing error
Comment thread internal/pkg/utils/login/login.go
@austin-denoble austin-denoble changed the title Add SSO connection routing for org-scoped login and pc target re-auth Add SSO support: two-round enforcement and pc target re-auth Apr 20, 2026
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b9ea0ec. Configure here.

Comment thread internal/pkg/utils/login/login.go
@austin-denoble austin-denoble changed the title Add SSO support: two-round enforcement and pc target re-auth Add SSO support: two-round enforcement and pc target re-auth Apr 20, 2026
@austin-denoble austin-denoble merged commit 7801515 into main Apr 20, 2026
8 checks passed
@austin-denoble austin-denoble deleted the adenoble/sso-support branch April 20, 2026 20:18
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