Typical Flows
This page covers the three most important flows: SP-initiated SSO, IdP-initiated SSO, and Single Logout (SLO). Before reading, it's recommended to familiarize yourself with Binding and Assertion structure in Core Concepts.
The examples below assume:
- SP Entity ID:
https://sp.example.com/saml/metadata, ACS address:https://sp.example.com/saml/acs - IdP Entity ID:
https://idp.example.org/saml/metadata, SSO address:https://idp.example.org/sso
SP-initiated SSO (Redirect + POST)
The most common combination: AuthnRequest uses HTTP-Redirect Binding, Response uses HTTP-POST Binding.
Step-by-step Flow
- User visits SP's protected resource
https://sp.example.com/app/dashboard; SP detects no local session. - SP generates
<AuthnRequest>, records itsID(for validatingInResponseTolater), stores the expected return deep link in RelayState (or stores locally and uses only a key in RelayState). - SP DEFLATE-compresses → Base64-encodes → URL-encodes the AuthnRequest and 302-redirects the browser to the IdP SSO endpoint with this as a query parameter.
- Browser requests IdP. IdP validates AuthnRequest (is Issuer a registered SP, is
AssertionConsumerServiceURLconsistent with Metadata, signature if required). - User completes authentication at IdP (enters password/MFA; if IdP already has a session, skips login page).
- IdP constructs
<Response>(containing a signed Assertion, withInResponseToset to the AuthnRequest's ID), Base64-encodes it, and places it in an auto-submitted form, returning to browser. - Browser automatically POSTs the form (
SAMLResponse+RelayState) to the SP's ACS (Assertion Consumer Service) endpoint. - SP completes all validations (see validation checklist below), creates a local session, and redirects to
https://sp.example.com/app/dashboardaccording to RelayState.
AuthnRequest Example
<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="_req_8f3a2b1c9d4e"
Version="2.0"
IssueInstant="2026-07-03T08:29:55Z"
Destination="https://idp.example.org/sso"
AssertionConsumerServiceURL="https://sp.example.com/saml/acs"
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST">
<saml:Issuer>https://sp.example.com/saml/metadata</saml:Issuer>
<samlp:NameIDPolicy
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
AllowCreate="true"/>
<samlp:RequestedAuthnContext Comparison="exact">
<saml:AuthnContextClassRef>
urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
</saml:AuthnContextClassRef>
</samlp:RequestedAuthnContext>
</samlp:AuthnRequest>
Redirect URL Encoding
Steps for generating the SAMLRequest parameter in Redirect Binding (order matters):
- DEFLATE compression: Perform raw deflate (RFC 1951, without zlib header, i.e., Python
zlib.compress(data, 9)[2:-4]orwbits=-15); - Base64 encoding (standard alphabet, not URL-safe variant);
- URL encoding (percent-encoding, because Base64 output contains
+,/,=).
GET /sso?SAMLRequest=fZJNb9swDIbv%2BRUC77bl2E1qIU6...
&RelayState=%2Fapp%2Fdashboard
&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256
&Signature=GtN2t7Jf...%3D HTTP/1.1
Host: idp.example.org
If signing the request: compute the signature on the string SAMLRequest=<v>&RelayState=<v>&SigAlg=<v> (each value in URL-encoded form, in this fixed order, only including parameters that are actually present), Base64-encode it, and attach as the Signature parameter. Redirect Binding signatures are not inside the XML.
Common Encoding Errors
- Forgot deflate, or used compression with zlib header → IdP reports "unable to inflate/parse request";
- Used URL-safe Base64 → decoding fails;
- Double-encoded already-encoded string, or framework auto-decoded then you decoded again →
%-related parsing errors.
Response Example
IdP sends Response back to SP via auto-submitted form:
POST /saml/acs HTTP/1.1
Host: sp.example.com
Content-Type: application/x-www-form-urlencoded
SAMLResponse=PHNhbWxwOlJlc3BvbnNlIC4uLg%3D%3D&RelayState=%2Fapp%2Fdashboard
After Base64-decoding SAMLResponse (POST Binding has only Base64, no deflate):
<samlp:Response
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="_resp_5e6f7a8b"
Version="2.0"
IssueInstant="2026-07-03T08:30:10Z"
Destination="https://sp.example.com/saml/acs"
InResponseTo="_req_8f3a2b1c9d4e">
<saml:Issuer>https://idp.example.org/saml/metadata</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<saml:Assertion ID="_asrt_9c0d1e2f" Version="2.0"
IssueInstant="2026-07-03T08:30:10Z">
<saml:Issuer>https://idp.example.org/saml/metadata</saml:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="#_asrt_9c0d1e2f">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>Kx7...=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>hR4k...=</ds:SignatureValue>
</ds:Signature>
<saml:Subject>
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">
[email protected]
</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData
Recipient="https://sp.example.com/saml/acs"
NotOnOrAfter="2026-07-03T08:35:10Z"
InResponseTo="_req_8f3a2b1c9d4e"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2026-07-03T08:29:40Z"
NotOnOrAfter="2026-07-03T08:35:10Z">
<saml:AudienceRestriction>
<saml:Audience>https://sp.example.com/saml/metadata</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2026-07-03T08:30:10Z"
SessionIndex="_sess_20260703_42">
<saml:AuthnContext>
<saml:AuthnContextClassRef>
urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="mail"
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue>[email protected]</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="displayName"
NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue>Alice Zhang</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</samlp:Response>
SP-side Validation Checklist (All Required)
Status/StatusCodeisSuccess;- Verify signature (using certificate registered in Metadata; per policy, Assertion and/or Response must be signed);
Issuerequals the expected IdP Entity ID;DestinationandSubjectConfirmationData/@Recipientequal this SP's ACS URL;InResponseTomatches an unconsumed AuthnRequest ID on this SP; immediately invalidate that ID after matching;Conditionstime window is valid,<Audience>contains this SP's Entity ID;- Assertion
IDhas not been processed before (replay prevention; cache seen IDs within the validity window).
Common Pitfalls
Common Pitfalls
- Clock skew: A few dozen seconds difference between IdP and SP clocks will trigger
NotBefore/NotOnOrAfterboundaries. Both sides should enable NTP; SP validation should allow 2–3 minutes of clock skew tolerance, but not 5 minutes or more. - Missing Audience validation: Without validating
<Audience>, an IdP-signed Assertion for app A can be used to log into app B (horizontal escalation on the same IdP). Audience comparison uses the SP Entity ID, not the ACS URL. - Missing InResponseTo validation: Without it, you cannot prevent replay/injection; attackers can inject captured Responses into arbitrary sessions. Note: IdP-initiated flow has no InResponseTo, so SP must clearly differentiate the two modes' validation policies.
- ACS URL using HTTP or wildcards: ACS must be an exact-match HTTPS address.
- RelayState open redirect: See Core Concepts.
IdP-initiated SSO
User clicks an application icon from the IdP portal (app list) and enters the SP directly, without an AuthnRequest phase.
Step-by-step Flow
- User logs into IdP portal, clicks target application icon;
- IdP directly constructs a signed Response for that SP (no
InResponseTo); RelayState is filled according to IdP-side configuration (typically the target URL); - Browser POSTs the form to the SP's ACS;
- SP validates and establishes session (skips InResponseTo validation; all other validations are the same as SP-initiated).
Security Considerations
Inherent Weakness of IdP-initiated
Because Response doesn't correspond to any SP-initiated request, SP cannot bind the message to its own flow through InResponseTo, therefore:
- Inherently cannot prevent Response injection/CSRF-style login (attacker can POST their legitimate Assertion into victim's browser, causing victim to "be logged in" to attacker's account, known as Login CSRF);
- Can only fallback to: extremely short Assertion validity period, strict single-use ID replay prevention, strict Audience/Recipient validation;
- If possible, disable it: If not required by business needs, SP should disable IdP-initiated by default. IdP portal icons can be configured to trigger SP-initiated flow (redirect to SP's login initiation URL) to achieve both UX and security.
Single Logout (SLO)
SLO aims to end the user's session at the IdP and all logged-in SPs. The messages are <LogoutRequest>/<LogoutResponse>, with frontend channels commonly using HTTP-Redirect Binding. The IdP relies on the SessionIndex and NameID in the login-time Assertion to locate the session to terminate.
SP-initiated SLO
- User clicks "logout" at SP1. SP1 terminates its local session and sends a signed
<LogoutRequest>to the IdP's SLO endpoint (Redirect Binding, containing NameID and SessionIndex); - IdP validates, then iterates over other SPs involved in that IdP session (SP2, SP3, …), sending
<LogoutRequest>to each via browser redirect; each SP terminates its session and returns<LogoutResponse>; - IdP terminates its own session and finally returns
<LogoutResponse>to the initiating SP1 (Status may be Success or PartialLogout); - SP1 displays "logged out" page.
Example LogoutRequest:
<samlp:LogoutRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="_lo_3c4d5e6f" Version="2.0"
IssueInstant="2026-07-03T09:00:00Z"
Destination="https://idp.example.org/slo">
<saml:Issuer>https://sp.example.com/saml/metadata</saml:Issuer>
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">
[email protected]
</saml:NameID>
<samlp:SessionIndex>_sess_20260703_42</samlp:SessionIndex>
</samlp:LogoutRequest>
IdP-initiated SLO
- User clicks "global logout" in IdP portal (or administrator forces logout);
- IdP sends
<LogoutRequest>to each SP involved in the session and collects<LogoutResponse>from each; - IdP terminates its own session and displays results.
Common Pitfalls
Common Pitfalls
- SLO has a notorious fragility problem: Frontend-channel SLO relies on the browser to redirect sequentially through all SPs; if any SP endpoint hangs/times out/has cert errors, the chain breaks and subsequent SPs don't receive the logout notification. Always handle and report
PartialLogoutstatus correctly; don't mislead users into thinking "all logged out." - SessionIndex not saved: If SP doesn't save
SessionIndexfrom the login Assertion into its local session, it can't fill it in LogoutRequest; some IdPs will refuse or log out all user sessions as a result. - LogoutRequest must be verified: Unsigned/unverified LogoutRequest can be weaponized as a DoS—attackers can arbitrarily kick users offline.
- Third-party cookie restrictions: Browser restrictions on third-party cookies in iframes can break iframe-based parallel SLO; prefer sequential redirect approach.
- Timeout fallback: Many real-world deployments end up with "local logout only + short IdP session timeout"; if adopted, explicitly communicate residual risk to security team (other SP sessions still active).
For item-by-item element/parameter meanings, see Typical Parameters and Message Reference.