SSO

Updated 6 interview angles 4 min read source
On this page7
  1. The vocabulary, which differs by protocol
  2. The flow, once
  3. Validating an OIDC callback
  4. SAML’s equivalent, and its extra hazard
  5. Provisioning and deprovisioning
  6. Deep dives
  7. Interview angle

SSO

One identity provider authenticates the user; many applications trust it. The security argument is not convenience — it is that credentials, MFA policy and offboarding live in one place. Disable an account at the IdP and access ends everywhere, immediately.

The vocabulary, which differs by protocol

Role SAML OIDC
Authenticates the user IdP OpenID Provider (OP)
The application Service Provider (SP) Relying Party (RP)
What it receives XML assertion ID token (JWT)

Same idea, different words. An interviewer switching between “SP” and “RP” is usually checking whether you notice they are the same role.

The flow, once

  1. User hits the app; no session, so redirect to the IdP.
  2. User authenticates at the IdP (MFA happens here, not in your app).
  3. IdP redirects back with a code or assertion.
  4. App validates it, then creates its own session.
  5. The next app redirects to the same IdP, which already has a session, and returns immediately. No second login — that is the SSO.

Step 4 is the whole security boundary, and it is where implementations fail.

Validating an OIDC callback

python
from authlib.jose import JsonWebToken, JWTClaims

def verify(id_token: str, nonce: str) -> JWTClaims:
    claims = JsonWebToken(["RS256"]).decode(
        id_token,
        # fetched, cached, rotated
        key=jwks,
        claims_options={
            "iss": {"essential": True, "value": ISSUER},
            "aud": {"essential": True, "value": CLIENT_ID},
        },
    )
    claims.validate(leeway=60)         # exp, iat, nbf
    if claims["nonce"] != nonce:       # replay defence
        raise BadNonce
    return claims

Each line is a real attack:

  • iss and aud — without them, a token the IdP issued for a different application is accepted by yours. This is token substitution, and it is the most common SSO vulnerability.
  • Algorithm allow-list (["RS256"]) — omit it and a token with alg: none or a symmetric algorithm using the public key as the HMAC secret validates.
  • leeway — clock skew between your host and the IdP rejects valid tokens otherwise. A minute is normal; hours means your NTP is broken.
  • nonce — bound to the session that started the flow, so a captured token cannot be replayed into a different browser session.

And the redirect URI must be an exact match against an allow-list, not a prefix:

python
ALLOWED = frozenset({
    "https://app.example.com/auth/callback",
    "https://staging.example.com/auth/callback",
})

def check(uri: str) -> str:
    # exact, not startswith
    if uri not in ALLOWED:
        raise BadRedirect(uri)
    return uri

startswith("https://app.example.com") accepts https://app.example.com.evil.com/cb, and the IdP will happily deliver the code there. Set membership has no such failure mode.

SAML’s equivalent, and its extra hazard

SAML validates a signature over XML rather than a JWT, which brings a problem JSON does not have: XML Signature Wrapping. An attacker moves the signed assertion into a decoy element and adds an unsigned one, and a parser that reads “the assertion” rather than “the assertion that was signed” accepts it.

The defence is a library that resolves the signature reference properly, not your own XML handling. This is the single strongest argument for never writing SAML parsing yourself — see SSO Attack Vectors.

Provisioning and deprovisioning

JIT provisioning creates the local account on first login from IdP attributes. It is convenient and it is only half a lifecycle: nothing deletes the account when the user leaves, because they simply stop logging in.

SCIM is the other half — the IdP pushes creates, updates and deletes to your app. If the interview is about a regulated environment, deprovisioning is the question they care about, and “JIT for creation, SCIM for the lifecycle” is the answer.

Deep dives

See SSO — Deep Topical Notes:

Interview angle 6

  • “What does SSO actually give you?” - one identity provider authenticates for many applications, so credentials, MFA policy and deprovisioning live in one place. The security win is centralised offboarding: disable once, access ends everywhere.
  • “SAML or OIDC?” - OIDC for anything new: JSON and JWT over HTTP, simpler to implement, native to mobile and SPAs. SAML is XML-based and entrenched in enterprise, so you will meet it whether or not you would choose it.
  • “What exactly do you validate on the token?” - issuer and audience (or a token minted for another app is accepted by yours), the signature against the IdP’s JWKS with an explicit algorithm allow-list, expiry with a small leeway for clock skew, and the nonce against the session that started the flow.
  • “Why does the algorithm allow-list matter?” - without it a token can arrive specifying none, or a symmetric algorithm that uses the IdP’s public key as an HMAC secret. Both validate against a naive implementation.
  • “What breaks in SSO integrations?” - clock skew invalidating assertions, certificate rotation on the IdP side, incorrect audience or issuer validation, redirect URIs matched by prefix instead of exactly, and group-to-role mapping drifting from what the IdP sends.
  • “JIT provisioning is enough, isn’t it?” - it covers creation only. Nobody is deleted when they leave; they just stop logging in. SCIM pushes the full lifecycle including deprovisioning, which is the half an auditor asks about.