Engineering Atlas › System Design Fundamentals

Signing in once with SSO

11 min read · last reviewed 2026-10-17

Open in the interactive app: practice, quiz, mock interview →

In one minute

How it works

Three sessions, not one

After SSO, a user has an IdP session (a cookie on the IdP's domain) and one local session per app (cookies on each app's domain). Browsers do not share cookies across domains, so the apps never see the IdP cookie. Instead each app sends the browser to the IdP, the IdP recognises its own cookie, and it sends the browser back with a signed statement of who the user is.

SP-initiated login with OIDC

  1. The user opens crm.example.com with no session. The app redirects to the IdP's authorize endpoint with scope=openid, a nonce and state.
  2. If the IdP session exists, there is no login prompt; the IdP immediately redirects back with an authorization code. If not, it authenticates the user (password, MFA, passkey) first.
  3. The app exchanges the code for an ID token, a JWT with iss, sub (stable user id), aud (this app), exp, nonce and auth_time.
  4. The app validates signature, issuer, audience, expiry and nonce, then creates its own session (OIDC Core §3.1.3.7).

OIDC Core and eight companion specs were also published as ISO/IEC standards in October 2024 (Core is ISO/IEC 26131:2024), which matters in procurement where only treaty-body standards count (OpenID Foundation).

SAML follows the same shape with different encoding: the IdP posts a signed XML Response containing an Assertion to the SP's assertion consumer service URL, with Audience, NotBefore and NotOnOrAfter conditions. Its XML signature handling is error-prone, one reason new systems prefer OIDC.

Logout

Logging out of one app ends only that app's session. Single logout needs the IdP to tell every app. Front-channel logout loads each app's logout URL in hidden frames, and those frames need the app's cookie in a third-party context. Safari has blocked third-party cookies by default since March 2020 (WebKit), while Chrome dropped its deprecation plan in April 2025 and kept them under user control (Google), so the same logout works in one browser and silently fails in another. The front-channel spec itself tells apps to detect this and warn the user (OIDC Front-Channel Logout §4.1). Back-channel logout has the IdP POST a signed logout token directly to each app's server, which then deletes sessions for that sub or session id. This requires apps to index sessions by user, which many do not.

Provisioning

SSO answers "who is this?", not "does this person have an account here, and in which groups?". Just-in-time provisioning creates the account on first login from token claims. SCIM lets the IdP push creates, updates and, crucially, deactivations, so a departing employee loses access everywhere even if an app session is still open (RFC 7644). A newer channel goes further: the OpenID Shared Signals Framework and its CAEP profile, final since 2025, let the IdP stream events such as session-revoked and credential-change to apps in near real time (CAEP 1.0).

Down the stack

Layer 1The contract

The IdP asserts, with its signature, that a subject authenticated at a given time, for one named audience, valid within a short window. The SP promises to create a session only from an assertion that is correctly signed by the expected issuer, addressed to itself, within its time window (plus a small clock skew), and not seen before. Login is therefore strongly specified. Logout is not: front-channel logout is best effort, and only back-channel logout or deprovisioning gives a server-to-server guarantee that sessions end.

Layer 2The mechanism

In SAML, the SP sends an AuthnRequest, deflated and base64-encoded into a query string (HTTP-Redirect binding, which every endpoint must support with DEFLATE (SAML Bindings §3.4.4)). The IdP answers with an auto-submitting HTML form that POSTs a base64 SAMLResponse to the SP's assertion consumer service URL (HTTP-POST binding). Inside, the Assertion carries Issuer, Subject/NameID, Conditions with NotBefore, NotOnOrAfter and AudienceRestriction, an AuthnStatement, attributes, and InResponseTo linking it to the request. The XML signature uses exclusive canonicalisation and a Reference URI pointing at the signed element's ID; signature wrapping attacks exploit code that verifies one element but reads another. A 2025 variant hit ruby-saml, which parsed the same response with two XML parsers that disagreed about which Signature element counted (see In the wild). OIDC replaces all this with a JWT id_token and a discovery document at /.well-known/openid-configuration. Back-channel logout POSTs a signed logout token with an events claim and a sid identifying the IdP session, or a sub, or both. The token must not carry a nonce, so it can never pass as an ID token, and the app answers 200 or 400 (OIDC Back-Channel Logout §2.4).

Layer 3The machine

A SAML response is often 5 to 15 KB once base64-encoded, too big for a URL (many servers cap request lines near 8 KB), which is why responses are POSTed while requests are deflated. Parsing, canonicalising and verifying XML costs milliseconds and needs a parser with external entities disabled. Every silent SSO hop is a chain of full browser navigations: two redirects, possibly a fresh TLS handshake to the IdP, and a token exchange, so 200 to 400 ms on a mobile network. Cookies matter at the byte level too: a browser cookie is limited to about 4 KB, and SameSite=Lax cookies are not sent on the cross-site POST back from the IdP, so an SP that keeps its pre-login state in such a cookie loses it unless it marks that cookie SameSite=None; Secure.

Why it's built this way

  1. Browsers isolate cookies per site, so apps cannot share a session and must bounce through the IdP.
  2. The browser is an untrusted courier between IdP and app, so assertions are signed, audience-bound, time-limited and replay-checked.
  3. Enterprises standardised on XML web services in the mid-2000s, giving SAML; the JSON and mobile era layered OIDC onto OAuth.
  4. Apps keep their own sessions for speed and to survive IdP blips, which turns revocation into a distributed problem solved by back-channel logout and SCIM.
  5. Centralising identity makes the IdP the single gate to everything, so it must be run as tier-zero infrastructure.

Diagram

sequenceDiagram
    participant B as Browser
    participant A1 as "App 1 (mail)"
    participant A2 as "App 2 (crm)"
    participant I as "Identity provider"
    B->>A1: GET /inbox
    A1-->>B: redirect to IdP
    B->>I: authorize (no IdP cookie)
    I-->>B: login + MFA, set IdP cookie
    B->>A1: callback with code, app session created
    B->>A2: GET /deals
    A2-->>B: redirect to IdP
    B->>I: authorize (IdP cookie present)
    I-->>B: redirect with code, no prompt
    B->>A2: callback, app session created

Worked example

A company of 8,000 employees uses 60 internal and SaaS applications behind one IdP.

Logins: each employee starts the day with one interactive login (password plus push MFA), then touches about 12 apps. That is 8,000 interactive logins and about 96,000 silent SSO redirects per day, peaking around 9 a.m. at perhaps 150 per second. Each silent redirect costs the user about 300 ms (two redirects and a token exchange), which is why apps keep local sessions for hours rather than re-checking every page.

Session lifetimes: IdP session 10 hours, app sessions 8 hours, ID token 5 minutes (used only to establish the session). If HR deactivates a user at 14:00, the IdP blocks new logins immediately, but open app sessions survive until they expire, up to 8 hours later. With SCIM deactivation pushed to the 25 apps that support it, and back-channel logout to the 30 that support that, exposure drops to the 5 apps that support neither, which the security team either replaces or puts behind a proxy that checks the IdP session every 15 minutes.

Availability: the IdP is now on the critical path for every new session. A 30-minute IdP outage at 9 a.m. blocks everyone who has not yet logged in, so the IdP needs multi-region deployment and its own incident runbook. Azure's Federated Identity pattern makes the same point: authentication becomes a single point of failure, so deploy identity in the same regions as the apps (Azure Architecture Center).

In the wild

Trade-offs

OptionStrengthsWeaknessesChoose when
OIDCJSON/JWT, mobile-friendly, simpler librariesFewer legacy enterprise integrationsNew apps, consumer and workforce
SAML 2.0Universal in enterprise SaaSXML signatures, complex, browser-onlyIntegrating with enterprise customers and legacy SaaS
Long app sessionsFewer redirects, resilient to IdP blipsSlow revocationLow-risk internal tools
Short app sessionsFast revocationMore IdP load and latencyAdmin and finance systems
Back-channel logoutReliable, server-to-serverApps must index sessions by userWorkforce SSO with offboarding risk

Pitfalls in production

Leadership lens

Check yourself

  1. Why does the second app not show a login prompt?

    Show answer

    The browser still holds the IdP's session cookie, so when the second app redirects there the IdP recognises the user and immediately issues a new assertion.

  2. Why does disabling a user at the IdP not immediately log them out of apps?

    Show answer

    Each app has its own local session that does not consult the IdP until it expires, unless the IdP pushes logout or deprovisioning.

  3. Why key users on sub rather than email?

    Show answer

    sub is stable and unique per issuer, while emails change and can be reassigned to a different person.

Sources and further study

Primary

Systems docs

Books

Other curricula

Watch

Free resources

Open in the interactive app: practice, quiz, mock interview →