Skip to content

SsoController

reference
1 min readUpdated

Kind: Controller

Source: atloria-monorepo/apps/api/src/auth/saml/sso.controller.ts

SSO handoff + discovery (A1). Public by design:

  • POST /auth/sso/exchange redeems a one-time code (minted after a validated SAML assertion) for the standard token pair — single use, so a leaked URL can't be replayed.
  • GET /auth/sso/discover maps an email domain to an SSO-enabled org so the login page can offer "Continue with SSO" from just an email.

SsoController exposes public SSO handoff endpoints for the authentication flow. It redeems validated, one-time SAML exchange codes for the standard token pair and discovers whether an email domain belongs to an SSO-enabled organization so clients can offer a “Continue with SSO” option.

Diagram

mermaid
graph LR
  Client[Login client] -->|GET /auth/sso/discover?email=user@company.com| SsoController
  SsoController --> DiscoveryService[SSO discovery service]
  DiscoveryService --> Organization[Organization SSO configuration]
  Organization --> DiscoveryService
  DiscoveryService --> Client

  SamlCallback[Validated SAML assertion] -->|Mints one-time code| ExchangeCode[One-time SSO code]
  Client -->|POST /auth/sso/exchange| SsoController
  SsoController --> ExchangeService[SSO exchange service]
  ExchangeService -->|Consumes code once| ExchangeCode
  ExchangeService --> TokenPair[Access and refresh tokens]
  TokenPair --> Client

Usage

ts
// Discover whether the user's organization supports SSO.
const discoveryResponse = await fetch(
  `/auth/sso/discover?email=${encodeURIComponent("user@acme.com")}`,
);

const discovery = await discoveryResponse.json();

if (discovery.ssoEnabled) {
  // Redirect or present the organization's SSO login option.
  window.location.assign(discovery.loginUrl);
}

// After the SAML callback redirects the client with a one-time code,
// exchange it immediately for the standard application token pair.
const exchangeResponse = await fetch("/auth/sso/exchange", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    code: new URLSearchParams(window.location.search).get("code"),
  }),
});

if (!exchangeResponse.ok) {
  throw new Error("SSO code exchange failed or was already used.");
}

const { accessToken, refreshToken } = await exchangeResponse.json();

AI Coding Instructions

  • Keep both endpoints public: SSO discovery and code redemption must be reachable before a user has an application session.
  • Treat exchange codes as short-lived, single-use credentials; consume them atomically and never allow a successful code to be redeemed twice.
  • Do not issue tokens directly from unvalidated SAML data in this controller; rely on the SAML validation and exchange services for assertion verification and token minting.
  • Preserve the standard authentication token response shape so SSO clients integrate identically to password or other login methods.
  • Avoid leaking organization or SSO configuration details during discovery beyond what the login client needs to offer the SSO flow.

Relationships

  • MODULE_DECLARES → exchange
  • MODULE_DECLARES → discover
  • DEPENDS_ON → SamlService
  • DEPENDS_ON → SsoCodeService

Referenced By

  • SamlModule (MODULE_DECLARES)

Was this page helpful?

Download as PDF