May 10, 2026 · Identity · 14 min read

How SAML Works — Flows, SSO, and the Assertion

Part 2 of the SAML From First Principles series

What if the application never sees your password at all?

Instead of you proving your identity to every application, what if you prove it once to a trusted authority, and that authority gives you a signed letter that says "I vouch for this person"?

Think of a passport. You don't prove your citizenship to every airline counter in every country. Instead, your government (a trusted authority) issues you a passport. The airline just checks: is this passport legitimate? Was it issued by a trusted government? If yes, you're in.

The airline never verifies your citizenship directly. It trusts the government to have done that already.

This is delegated authentication. The application delegates the job of verifying your identity to someone else.

Two key players

This model introduces two roles.

The Identity Provider (IdP) is the trusted authority. It's the entity that actually verifies who you are. Think of it as the government that issues passports. In the enterprise world, this is something like Okta, Azure AD, or Ping Identity.

The Service Provider (SP) is the application. It's the entity that wants to know who you are but doesn't want to verify it directly. Think of it as the airline that checks passports. In the enterprise world, this is Slack, Jira, Salesforce, or any other application.

The IdP verifies your identity. The SP trusts the IdP's verification. The user logs in at the IdP once, and accesses the application without logging in again.

How trust is established

The airline doesn't trust just any passport. It trusts passports from specific governments. Similarly, the SP doesn't trust just any IdP. Trust must be explicitly established between the two.

This trust is established using certificates and digital signatures. The IdP has a private key that it uses to sign its assertions (the "passport"). The SP has the IdP's public key (the "certificate") that it uses to verify those signatures.

If the signature checks out, the SP knows the assertion came from the real IdP and hasn't been tampered with. If the signature doesn't check out, the SP rejects it.

No certificate exchange, no trust, no access. Both sides must explicitly configure and trust each other.

SP-Initiated Flow

This is the most common flow. You go to the application first, and it redirects you to the IdP.

The following diagram shows the complete SP-initiated flow:

SP-Initiated SAML Flow

Step by step:

  1. You open your browser and go to slack.com.
  2. Slack doesn't know who you are. Slack generates a SAML AuthnRequest and redirects your browser to the IdP's SSO URL.
  3. Your browser follows the redirect to the IdP (e.g., login.okta.com). The AuthnRequest tells the IdP "Slack is asking you to authenticate this user."
  4. The IdP checks if you have an existing session cookie. If not, it shows a login page.
  5. You enter your credentials. The IdP validates them.
  6. The IdP generates a SAML Response containing a signed assertion. It sends this back to your browser as an auto-submitting HTML form that POSTs to Slack's ACS URL.
  7. Your browser automatically submits the form to Slack's Assertion Consumer Service endpoint.
  8. Slack validates the signature, reads your identity, creates a session, and grants access.

If you already authenticated at the IdP earlier, steps 4 and 5 are skipped. The IdP sees your session cookie, generates the assertion immediately, and sends it back. You never see a login page.

IdP-Initiated Flow

The other way SAML can start. Instead of going to the application first, you start at the identity provider.

IdP-Initiated SAML Flow

You're already logged into your Okta dashboard. You see tiles for all your applications. You click the Slack tile. The IdP generates the assertion and sends it directly to Slack. No redirect dance. No AuthnRequest from the SP.

SP-initiated is more common and more secure because the SP generates the AuthnRequest with a unique ID to prevent replay attacks. IdP-initiated is simpler but the SP receives an assertion it didn't ask for.

The SSO magic

Here's what makes single sign-on feel like magic:

SSO Session Flow

You log in to your identity provider once. The IdP creates a session cookie in your browser for its domain. Now you go to Slack. Slack redirects to the IdP. Your browser sends the session cookie automatically. The IdP sees it, skips the login page, and sends the assertion back immediately.

Open Jira. Same thing. Gmail. Salesforce. Every application. One login, and you're in everywhere.

Three things make this work:

  1. The browser stores cookies per domain. Every redirect to idp.company.com includes the cookie automatically.
  2. The IdP checks for an existing session before showing a login page. Cookie present and valid? Skip login, generate assertion.
  3. Each SP creates its own session after receiving a valid assertion. Subsequent visits don't even go back to the IdP.

Inside the SAML Assertion

The assertion is the core of the protocol. It's the "passport" that carries your identity from the IdP to the SP.

SAML Assertion Structure

An assertion contains:

Here's what the XML looks like (simplified):

<saml:Assertion>
  <saml:Issuer>https://idp.company.com</saml:Issuer>
  <saml:Subject>
    <saml:NameID>alice@company.com</saml:NameID>
  </saml:Subject>
  <saml:Conditions
    NotBefore="2026-03-29T10:00:00Z"
    NotOnOrAfter="2026-03-29T10:05:00Z">
    <saml:AudienceRestriction>
      <saml:Audience>https://slack.com</saml:Audience>
    </saml:AudienceRestriction>
  </saml:Conditions>
  <saml:AttributeStatement>
    <saml:Attribute Name="Role">
      <saml:AttributeValue>Admin</saml:AttributeValue>
    </saml:Attribute>
    <saml:Attribute Name="Department">
      <saml:AttributeValue>Engineering</saml:AttributeValue>
    </saml:Attribute>
    <saml:Attribute Name="Groups">
      <saml:AttributeValue>platform-team</saml:AttributeValue>
      <saml:AttributeValue>security-reviewers</saml:AttributeValue>
    </saml:Attribute>
  </saml:AttributeStatement>
  <ds:Signature>...</ds:Signature>
</saml:Assertion>

The SP validates the signature, checks the time window, verifies the audience matches itself, reads the NameID to identify the user, and reads attributes to determine permissions.

Signing vs Encrypting

Signing proves the assertion is authentic and unmodified. The IdP signs with its private key. The SP verifies with the IdP's public key. Always required.

Encryption adds confidentiality. The assertion travels through the user's browser. If someone intercepts it, they can read it (signing doesn't hide content). Encryption uses the SP's public key so only the SP can decrypt it.

Most deployments use signing only, relying on TLS for confidentiality. High-security environments add assertion encryption as defense in depth.

Previous: The Problem SAML Solves

Next: SAML Bindings, Metadata, Setup, and Single Logout

← Back to all posts