Skip to content

SSO sign-in success returns tokens as JSON instead of session cookies and a redirect #803

Description

@alex-dembele

Problem (user terms)

When someone signs in with Google, GitHub, Entra (OAuth) or SAML and it
succeeds, they never reach the application. The browser is in the middle of
a redirect from the identity provider and gets a raw JSON page back, with the
access and refresh tokens printed on screen. The user is stuck on that page, and
the session tokens are delivered in a script-readable body when every other
login path puts them in HttpOnly cookies.

Found while working on #295 (PR #802).

What the code does today

  • backend/internal/handler/sso_session.go:96 issueSSOSession ends with
    c.JSON({access_token, refresh_token, …}) (lines 127-139). It never calls
    middleware.IssueSessionCookies.
  • Two callers: oauth2_handler.go (OAuth callback, a browser GET redirected by
    the provider) and saml2_handler.go:240 (SAML ACS, a browser POST from the IdP).
    Both are top-level navigations, not XHR.
  • Password and MFA login (handler/auth/mfa_handler.go:151) already do it right:
    IssueSessionCookies sets or_access, or_refresh and or_csrf.
  • The error branches in issueSSOSession also return JSON with a 500, which is a
    dead end for a navigating browser. Every other OAuth exit redirects to
    /login?error=<code> (see oauthFailure).
  • The OAuth flow stores ReturnTo (sanitised, oauth2_handler.go) but nothing
    reads it after a successful sign-in.

Acceptance criteria

  1. On success, issueSSOSession sets the session with
    middleware.IssueSessionCookies (same TTLs as password login) and answers
    with a 302 to the SPA, not a JSON body.
  2. No response from issueSSOSession carries access_token or refresh_token
    in the body or in the redirect URL (query or fragment).
  3. The redirect target is the OAuth flow's sanitised ReturnTo when it is set,
    otherwise the SPA home (oauthAppBaseURL). An off-site target is impossible.
    sanitiseReturnTo stays the only gate.
  4. Every failure inside issueSSOSession (onboarding, session issuance)
    redirects to /login?error=internal, never a JSON 500.
  5. SAML ACS gets the same behaviour (cookies + redirect), and the POST→302 hop
    still works.
  6. Once redirected, the SPA starts signed in with no manual step: it loads the
    session from the cookies (/auth/me), including the CSRF token for
    mutations. Verify this in a real browser. If the SPA cannot start from
    cookies alone, the frontend change is part of this issue.
  7. Audit and last-login behaviour are unchanged (AuditActionLogin still
    logged).
  8. Tests: success sets the three cookies and redirects with no token in the
    body; ReturnTo is honoured and an off-site value is ignored; each failure
    branch redirects with error=internal; SAML path covered.

Definition of done

  • Backend tests green (go test ./... -count=1), go vet clean.
  • Live proof: the branch server plus the SPA, one full OAuth sign-in against the
    stub provider or a real one, landing signed in on the dashboard.
    Include a screenshot or a command transcript, together with a passing test.
  • docs/API_SECURITY_GUIDE.md OAuth2/SAML2 flow updated. Its step 5 currently
    says only "JWT token issued".
  • PR with Closes #<n> and an issue checkpoint comment.

Out of scope

  • The in-process OAuth state store (single-replica limit, noted in
    oauth_state_service.go).
  • Any change to MFA enforcement on SSO sign-in.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:backendGo, /internal, /pkgarea:enterpriseEnterprise platform capabilitiespriority:P1Highpriority:P1-highBlocks a milestonestatus:readyMeets the ready definitiontier:0-trustTrust: security, isolation, evidence integritytrustEvidence a buyer's CISO tests before features mattertype:bugSomething is broken

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions