SSO and SAML: Stop Copy-Pasting Auth Code and Fix Your Login Once
SSO and SAML explained for production engineers.
20+ years shipping large-scale distributed systems. Notes here come from systems that actually shipped.
- ✓Solid grasp of fundamentals
- ✓Comfortable reading code examples
- ✓Basic production concepts
SAML is an XML-based protocol for exchanging authentication and authorization data between an identity provider and a service provider. It's the backbone of enterprise SSO. You configure an IdP (like Okta or ADFS) to issue SAML assertions, and your app (the SP) validates those assertions to grant access.
Think of SAML like a VIP pass at a conference. You show your ID at the front desk (the identity provider), and they give you a stamped wristband (the SAML assertion). You then walk into any session (service provider) and just flash the wristband — no need to show your ID again. The wristband has a hologram (digital signature) so no one can fake it.
Every company I've worked at has had a 'login incident' that woke someone up at 3 AM. Usually it's because someone copy-pasted SAML code from a blog post without understanding the clock skew check. SSO is supposed to make life easier, but misconfigured SAML is a silent killer — users can't log in, and you have no idea why because the error messages are garbage.
The problem SAML solves is simple: you don't want every app to manage its own passwords. You want one central authority (the IdP) that says 'this user is authenticated' and every other app trusts that. Without SAML, you either build a custom SSO protocol (please don't) or force users to log in to each app separately (which they hate).
By the end of this, you'll be able to configure SAML SSO in a production app, debug the three most common failures (clock skew, unsigned assertions, and wrong audience), and explain to your boss why you chose SAML over OIDC without sounding like a Wikipedia article.
Why SAML Exists: The Password Proliferation Problem
Before SAML, every app had its own login. Users had 50 passwords, so they reused 'password123' everywhere. IT admins couldn't revoke access centrally — they had to delete accounts in 20 different systems. SAML solved this by separating authentication (the IdP's job) from authorization (the SP's job). The IdP tells the SP 'this user is who they say they are' via a signed XML document. The SP trusts that document because it's signed with the IdP's private key.
The key insight: SAML is about trust, not just data exchange. The SP doesn't ask the IdP 'is this user valid?' every time. Instead, the IdP gives the SP a self-contained assertion that the SP can verify independently. This means the SP doesn't need network access to the IdP at runtime — it just needs the IdP's public key and a valid clock.
Without SAML, you'd have to build a shared session database or use something like OAuth 2.0 with a token introspection endpoint (which requires network calls). SAML's assertion model is more resilient to network failures, but it's also more complex to debug because the assertion is a blob of XML with strict schema rules.
echo '<base64>' | base64 -d | xmllint --format -. You'll see the actual timestamps, issuer, and signature — 90% of issues are visible right there.SAML vs OIDC: When to Use Each in Production
The classic mistake: using SAML for a mobile app. SAML was designed for browser-based SSO using HTTP POST bindings. It doesn't work well with native apps because there's no browser redirect flow that returns to the app. OIDC (OpenID Connect) is built for this — it uses JWTs and REST APIs.
Use SAML when: (1) You're in an enterprise environment with existing IdPs like ADFS, Okta, or Azure AD. (2) You need to support SAML-based federation (e.g., government or healthcare). (3) Your app is web-based and users access it via browsers.
Use OIDC when: (1) You're building a new app from scratch. (2) You have mobile or single-page apps. (3) You want simpler JSON tokens instead of XML. (4) You need fine-grained scopes and API access.
The trade-off: SAML is more mature and has better enterprise support, but OIDC is simpler and more modern. I've seen teams waste weeks trying to make SAML work in a mobile app — don't be that team.
The SAML Handshake: AuthnRequest and Response Flow
The SAML flow starts when an unauthenticated user tries to access a protected resource on the SP. The SP generates an AuthnRequest — an XML document that tells the IdP what the SP expects (e.g., which SAML version, what binding to use, and where to send the response). The SP redirects the user to the IdP with this request.
The IdP authenticates the user (via password, MFA, etc.) and generates a SAML Response. This response contains an Assertion — the actual statement that the user is authenticated. The assertion includes: the user's identifier (NameID), the issuer (IdP entity ID), the audience (SP entity ID), timestamps (NotBefore and NotOnOrAfter), and optionally attributes (email, roles).
The IdP signs the assertion (or the entire response) with its private key. The SP validates the signature using the IdP's public certificate. If the signature is valid, the timestamps are within range, and the audience matches, the SP creates a local session and redirects the user to the original resource.
The critical detail: the SP must have the IdP's public certificate configured. If the certificate changes (e.g., rotation), all SPs must be updated. This is a common source of production outages.