Skip to content

SAML SSO Flow Diagram: SP-Initiated and IdP-Initiated Login

How SAML 2.0 single sign-on works: the SP-initiated and IdP-initiated flows, what a SAML response contains, what your app must check, and where it breaks.

· Founder & developer

Published

Sequence diagram with a user, a browser, a service provider and an identity provider. The app redirects to the IdP with an AuthnRequest; an optional fragment shows login and MFA; the IdP returns a form that posts the SAMLResponse to the ACS URL; the app validates it and sets a cookie.

The first large customer who signs a contract will ask for single sign-on with their identity provider, and very often that means SAML 2.0. The standard dates from 2005, it moves XML through browser redirects and form posts, and its failure messages range from vague to silent. Most SAML bugs, including serious security holes, come from not knowing exactly which message goes where and what the service provider must check.

This post walks through the SP-initiated flow message by message with a sequence diagram, then the IdP-initiated variant and its risks, the anatomy of a SAML response, the validation checklist for your app, metadata and certificate rotation, and how SAML compares with OpenID Connect.

The three parties

Party In SAML terms Examples
The user's browser User agent Any browser
Your application Service provider (SP) Your SaaS app
The customer's login system Identity provider (IdP) Microsoft Entra ID, Okta, Google Workspace, AD FS

In the common Web Browser SSO profile, the SP and the IdP never talk to each other directly. Every message travels through the browser, as a redirect or an auto-submitted form. What they share is configuration, exchanged once as metadata: each side's entity ID, the IdP's sign-in URL, the SP's Assertion Consumer Service (ACS) URL and the IdP's signing certificate.

The SP-initiated flow, step by step

The user starts at your app:

  1. The user opens https://app.example.com/dashboard without a session.
  2. Your app creates an AuthnRequest with a unique ID, its entity ID and its ACS URL, stores the ID in the pending login state, and redirects the browser to the IdP. With the HTTP-Redirect binding, the request travels in the query string as SAMLRequest (deflated, base64-encoded), next to a RelayState value that remembers where the user wanted to go.
  3. The IdP authenticates the user: password and MFA, or an existing IdP session, in which case the user sees nothing.
  4. The IdP returns an HTML page with a form that posts a signed SAMLResponse and the same RelayState to your ACS URL. This is the HTTP-POST binding, and a small script submits the form automatically.
  5. Your app validates the response, creates its own session cookie and redirects the user to the page saved in RelayState.

From then on, your app's own session decides who the user is. SAML is only involved again when that session expires.

The bindings specification limits RelayState to 80 bytes, so store the real return URL server-side and send a short key.

What a SAML response contains

Trimmed to the parts your app reads, a response looks like this:

<samlp:Response ID="_r81c" InResponseTo="_req42" Destination="https://app.example.com/saml/acs">
  <saml:Assertion ID="_a77e">
    <saml:Issuer>https://idp.customer.com/entity</saml:Issuer>
    <ds:Signature>…</ds:Signature>
    <saml:Subject>
      <saml:NameID>ana@customer.com</saml:NameID>
      <saml:SubjectConfirmationData InResponseTo="_req42"
        Recipient="https://app.example.com/saml/acs" NotOnOrAfter="2026-11-02T09:05:00Z"/>
    </saml:Subject>
    <saml:Conditions NotBefore="2026-11-02T08:59:30Z" NotOnOrAfter="2026-11-02T09:05:00Z">
      <saml:AudienceRestriction><saml:Audience>https://app.example.com</saml:Audience></saml:AudienceRestriction>
    </saml:Conditions>
    <saml:AttributeStatement>…email, name, groups…</saml:AttributeStatement>
  </saml:Assertion>
</samlp:Response>

The assertion is the signed statement: who the user is (NameID), who says so (Issuer), for whom (Audience), for how long, and in answer to which request (InResponseTo).

The validation checklist

Your ACS endpoint is where SAML security succeeds or fails. It must:

  1. Verify the signature with the certificate from the IdP's metadata, never with a certificate embedded in the message itself.
  2. Read only what the signature covers. XML signature wrapping attacks insert a second, unsigned assertion and hope the code verifies one element and reads another. Researchers found this flaw in 11 of 14 major SAML frameworks in 2012, and it keeps coming back: ruby-saml fixed a signature bypass rated CVSS 10.0 (CVE-2024-45409) in 2024 and two parser-differential bypasses (CVE-2025-25291 and CVE-2025-25292) in 2025.
  3. Check Issuer against the IdP's entity ID for this customer.
  4. Check Audience against your own entity ID.
  5. Check Destination and Recipient against your ACS URL.
  6. Check NotBefore and NotOnOrAfter with a tolerance of a minute or two for clock skew.
  7. Match InResponseTo to an AuthnRequest ID you issued for this browser and have not used yet.
  8. Reject replays by remembering assertion IDs until they expire.
  9. Treat RelayState as untrusted. Redirect only to relative paths or an allowlist, or you have an open redirect.

The lesson from a decade of SAML vulnerabilities: use a maintained library, pin it, patch it quickly, and never write your own XML signature verification.

IdP-initiated SSO and why it is riskier

In the IdP-initiated flow the user starts at the IdP, usually by clicking your app's tile in a dashboard such as the Okta or Microsoft My Apps portal. The IdP posts an unsolicited response to your ACS URL. There was no AuthnRequest, so there is no InResponseTo to match.

SP-initiated IdP-initiated
Starts at Your app The IdP's portal
AuthnRequest Yes No
InResponseTo check Yes Not possible
Login CSRF protection Built in Needs extra care
Deep links Through RelayState Usually lands on a default page

Without InResponseTo, an attacker who obtains a valid response, or who gets a victim's browser to post one, can sign the victim into the attacker's account or replay a stolen response within its validity window. If customers need dashboard tiles, the safer pattern is a tile that opens your app's login URL, which then starts a normal SP-initiated flow. If you must accept unsolicited responses, keep validity short, enforce one-time assertion IDs, and allow it per customer rather than globally.

Metadata, certificates and multi-tenant setups

Each customer's IdP is a separate trust relationship. Store per tenant: the IdP entity ID, sign-in URL, signing certificates and attribute mapping. The multi-tenant SaaS architecture diagram shows where this per-tenant configuration usually lives.

Signing certificates expire, and an expired or rotated certificate is the most common cause of a whole customer suddenly unable to sign in. Accept two certificates per IdP during a rollover, read the IdP's metadata URL on a schedule if it publishes one, and alert weeks before expiry.

SAML authenticates; it does not keep your user list in sync. Creating accounts on first login is easy, but removing access when someone leaves needs provisioning, usually SCIM 2.0 (RFC 7644). Without it, a deactivated employee keeps any session your app already issued.

SAML or OpenID Connect?

SAML 2.0 OpenID Connect
Format XML assertions with XML signatures JSON, with JWT ID tokens
Transport Browser redirects and form posts Redirects plus a back-channel token request
Mobile apps and SPAs Awkward Designed for them
Typical use Enterprise SSO into SaaS apps Consumer login, APIs, modern enterprise SSO

New apps can start with OpenID Connect, but enterprise buyers will keep asking for SAML, so most B2B products support both. The OAuth 2.0 flow diagram covers the protocol OpenID Connect builds on.

The SAML SSO diagram

Sequence diagram with a user, a browser, a service provider and an identity provider. The app redirects to the IdP with an AuthnRequest; an optional fragment shows login and MFA; the IdP returns a form that posts the SAMLResponse to the ACS URL; the app validates it and sets a cookie.

6 components · 11 connections

Open in Archboard
Diagram contents
  • User
  • Browser
  • Service provider (your app)
  • Identity provider
  • no IdP session yet
  • 1. Open the app
  • 2. GET /dashboard (no session)
  • 3. 302 to IdP: SAMLRequest + RelayState
  • 4. GET /sso?SAMLRequest=…&RelayState=… (HTTP-Redirect)
  • 5. Login page
  • 6. Password + MFA
  • 7. HTML form: signed SAMLResponse + RelayState
  • 8. POST /saml/acs (HTTP-POST, auto-submit)
  • 9. 302 /dashboard + session cookie
  • 10. GET /dashboard (cookie)
  • 11. 200 OK
SP-initiated SAML single sign-on. The app redirects the browser to the identity provider with an AuthnRequest, the IdP signs the user in and posts a signed response back to the app's ACS URL, and the app validates it before creating its own session.

How to read it:

  • Four participants. The user, the browser, your app as the service provider and the identity provider. Time runs downward.
  • The redirect carries the AuthnRequest and RelayState from your app to the IdP, through the browser.
  • The optional fragment is the login step. With an existing IdP session it is skipped and the user sees only a quick redirect.
  • The form post delivers the signed SAMLResponse to the ACS URL. Your app validates it and sets its own session cookie.
  • The Markdown block lists the checks the ACS endpoint must make.

Drawing SSO flows that help with debugging

  • Draw the browser as its own participant. SAML is a front-channel protocol; hiding the browser hides where messages can be intercepted or replayed.
  • Name the binding on each arrow: redirect or POST, and the parameter name. Most configuration errors are a wrong URL or binding.
  • Put IDs on the messages. Showing that InResponseTo echoes the request ID explains why it is checked.
  • Draw the IdP-initiated flow separately, with its risk noted, instead of as a branch of the main diagram.

The sequence diagram example covers participants, fragments and labels in more depth, and the zero trust architecture diagram shows where the identity provider sits in a broader access model.

Build it in Archboard

  1. Sign in with Google or GitHub and create a diagram.
  2. Add an Actor from the Sequence pack for the user, then three Lifeline nodes: Browser, Service provider and Identity provider.
  3. Draw the messages top to bottom as edges between lifelines: the first request, the redirect with SAMLRequest and RelayState, the request to the IdP, the form with SAMLResponse, the POST to the ACS URL and the final redirect with the session cookie.
  4. Add a Fragment from the Sequence pack with the operator opt around the login and MFA step.
  5. Add a Markdown block from the Core pack with the validation checklist.

The example uses 6 diagram nodes, within the Free plan.

FAQ

What is the difference between SP-initiated and IdP-initiated SSO? SP-initiated login starts at your app, which sends an AuthnRequest to the IdP. IdP-initiated login starts in the IdP's portal, which posts an unsolicited response to your app. The second has no request to match, which weakens protection against replay and login CSRF.

What is an ACS URL? The Assertion Consumer Service URL is the endpoint in your app that receives the SAML response by HTTP POST. It is configured in the IdP and must match the Destination and Recipient values in each response.

What is RelayState used for? It carries the place the user wanted to reach, so your app can send them there after login. It is limited to 80 bytes and is untrusted input, so use it as a key to server-side state or validate it against an allowlist.

Is SAML outdated? It is older than OpenID Connect and harder to implement safely, but it remains the standard many enterprise identity providers and buyers expect. Most B2B products support both.

Does SAML remove users when they leave the company? No. SAML only authenticates at login. Deprovisioning needs SCIM or another provisioning integration, plus reasonable session lifetimes in your app.

Start free — one seat, no credit card.

Related posts

← Back to blog

Open a canvas in a minute

Sign in with Google or GitHub. Free for a single seat. Upgrade when the team joins.