A comprehensive guide to Security Assertion Markup Language – the enterprise standard for Single Sign-On (SSO) authentication.
SAML (Security Assertion Markup Language) is an XML-based open standard for exchanging authentication and authorization data between parties. Developed by OASIS, SAML 2.0 (released in 2005) is the current version widely used in enterprise environments.
SAML enables Single Sign-On (SSO) – users authenticate once with an Identity Provider and can access multiple Service Providers without re-entering credentials. This is especially valuable in enterprise environments with many applications.
The authority that authenticates users and issues SAML assertions. Examples: Okta, Azure AD, OneLogin, ADFS, Ping Identity.
The application that relies on the IdP for authentication. It consumes SAML assertions to grant access. Examples: Salesforce, Slack, AWS.
The end user who wants to access a service. They authenticate with the IdP and are redirected to the SP with a SAML assertion.
Both IdP and SP publish metadata XML files containing:
There are two main flows: SP-Initiated (most common) and IdP-Initiated.
User tries to access a protected resource on the Service Provider (e.g., app.example.com).
SP creates a SAML AuthnRequest and redirects user to IdP with the request (via HTTP-Redirect or HTTP-POST binding).
IdP presents login page. User enters credentials (or uses existing session if already logged in).
IdP creates a SAML Response containing an Assertion with user attributes and signs it with its private key.
User's browser POSTs the SAML Response to the SP's Assertion Consumer Service (ACS) URL.
SP verifies the signature, checks conditions (time validity, audience), extracts user info, and creates a local session.
In IdP-initiated SSO, the user starts at the IdP portal and clicks on an application. The IdP sends an unsolicited SAML Response to the SP. This flow is less secure as there's no AuthnRequest to tie the response to, making replay attacks easier.
Bindings define how SAML messages are transported between IdP and SP.
Messages are Base64-encoded and sent in HTML form fields. Supports large payloads (signed responses with assertions).
<form method="POST" action="https://sp.example.com/acs">
<input type="hidden" name="SAMLResponse" value="PHNhbWxwOl..."/>
<input type="hidden" name="RelayState" value="..."/>
</form>
Messages are DEFLATE-compressed, Base64-encoded, and URL-encoded in query parameters. Used for AuthnRequests.
https://idp.example.com/sso?
SAMLRequest=fZJNT8Mw...&
RelayState=...&
SigAlg=...&
Signature=...
Direct server-to-server communication using SOAP. Used for Artifact Resolution and Attribute Queries. Not browser-based.
Instead of sending the full message, a small artifact (reference) is passed. SP resolves the artifact via back-channel to retrieve the actual message.
| Message | Sender | Purpose |
|---|---|---|
AuthnRequest |
SP → IdP | Request authentication of a user |
Response |
IdP → SP | Contains authentication result and user assertions |
LogoutRequest |
SP/IdP | Request termination of user session (Single Logout) |
LogoutResponse |
SP/IdP | Acknowledge logout request |
AttributeQuery |
SP → IdP | Request additional user attributes |
ArtifactResolve |
SP → IdP | Retrieve full message from artifact reference |
<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
ID="_abc123"
Version="2.0"
IssueInstant="2025-01-28T10:00:00Z"
Destination="https://idp.example.com/sso"
AssertionConsumerServiceURL="https://sp.example.com/acs"
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST">
<saml:Issuer>https://sp.example.com</saml:Issuer>
<samlp:NameIDPolicy
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
AllowCreate="true"/>
</samlp:AuthnRequest>
An Assertion is the core of SAML – it contains statements about the user made by the IdP.
Confirms the user was authenticated at a specific time using a specific method (password, MFA, certificate, etc.).
Contains user attributes like email, name, groups, roles. These are used by SP for authorization decisions.
States whether the user is permitted/denied access to a specific resource. Rarely used in practice.
<saml:Assertion ID="_xyz789" IssueInstant="2025-01-28T10:00:30Z">
<saml:Issuer>https://idp.example.com</saml:Issuer>
<ds:Signature>...</ds:Signature>
<saml:Subject>
<saml:NameID Format="emailAddress">[email protected]</saml:NameID>
<saml:SubjectConfirmation Method="bearer">
<saml:SubjectConfirmationData
NotOnOrAfter="2025-01-28T10:05:30Z"
Recipient="https://sp.example.com/acs"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="..." NotOnOrAfter="...">
<saml:AudienceRestriction>
<saml:Audience>https://sp.example.com</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="...">
<saml:AuthnContext>
<saml:AuthnContextClassRef>PasswordProtectedTransport</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="firstName">
<saml:AttributeValue>John</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="groups">
<saml:AttributeValue>admin</saml:AttributeValue>
<saml:AttributeValue>users</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
emailAddress | User's email address |
persistent | Opaque, stable identifier across sessions |
transient | Temporary identifier, changes per session |
unspecified | Any format, IdP decides |
SAML uses XML Digital Signatures (XMLDSig) to ensure message integrity and authenticity.
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="...xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="...rsa-sha256"/>
<ds:Reference URI="#_assertionId">
<ds:Transforms>
<ds:Transform Algorithm="...enveloped-signature"/>
<ds:Transform Algorithm="...xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="...sha256"/>
<ds:DigestValue>base64-encoded-digest</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>base64-encoded-signature</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>base64-encoded-cert</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
NotBefore |
Assertion is not valid before this time |
NotOnOrAfter |
Assertion expires at this time (typically 5 minutes) |
SessionNotOnOrAfter |
When to force re-authentication |
Clock Skew: Allow 1-2 minutes tolerance for server time differences.
| Aspect | SAML 2.0 | OAuth 2.0 | OpenID Connect |
|---|---|---|---|
| Purpose | Authentication + Attributes | Authorization | Authentication (on top of OAuth) |
| Format | XML | JSON | JSON + JWT |
| Token | SAML Assertion | Access Token | ID Token (JWT) |
| Transport | Browser redirects/POSTs | HTTP APIs | HTTP APIs |
| Best For | Enterprise SSO, Legacy | API Authorization, Mobile | Modern SSO, Consumer Apps |
| Complexity | High | Medium | Medium |
Use these tools to work with SAML messages:
Sign SAML XML messages with your private key
Verify signatures and decode Base64/deflated messages
Parse and decode X.509 certificates
RSA key generation, encryption, signing
Encode and decode Base64 strings
Generic XML digital signature tools
Every coffee helps keep the servers running. Every book sale funds the next tool I'm dreaming up. You're not just supporting a site — you're helping me build what developers actually need.