---
title: "What are SAML assertions?"
description: "A complete technical guide to understanding SAML assertions, covering their structure, responses, lifecycle, common errors, debugging best practices, and step-by-step implementation of SAML SSO using WorkOS."
url: https://workos.com/blog/saml-assertions-complete-guide
published: "Jul 30, 2025"
---

# What are SAML assertions?

If you’ve ever set up Single Sign-On (SSO) for an application, you’ve probably bumped into the term SAML assertion.

They sound intimidating, but in reality, they’re just chunks of XML that carry authentication and authorization data between an [**Identity Provider (IdP)**](https://workos.com/blog/what-is-an-identity-provider) and a [**Service Provider (SP)**](https://workos.com/blog/idp-vs-sp-what-is-a-service-provider-and-an-identity-provider#what-is-a-service-provider).

In this article, we’ll break down what SAML assertions are, why they matter, and even walk through an example.

## **The basics: SAML and assertions**

[SAML (Security Assertion Markup Language)](https://workos.com/guide/what-is-saml-and-how-does-it-work) is an **XML-based, OASIS standard** that defines how identity information is securely exchanged between systems. It’s primarily used in federated identity scenarios (think logging into Salesforce using your corporate credentials).

A SAML assertion is a **security token** that’s:

- **Issued by an Identity Provider (IdP)**: A system like Okta, Azure AD, or OneLogin that authenticates users.
- **Consumed by a Service Provider (SP)**: The application or service the user is trying to access.
- **Transmitted over protocols like HTTP POST or HTTP Redirect**, usually encoded in Base64 and embedded in an HTML form. Assertions can also be transmitted over SOAP (used in some back-channel communications).

### **Structure of a SAML assertion**

Each assertion is an XML document that follows this hierarchy:

- **Envelope**: *\<saml:Assertion>*
  - Contains metadata like `ID`, `IssueInstant`, `Version`, and `Issuer`.
  - Assertions carry a unique ID and are short-lived to prevent reuse.
- **Subject**:
  - Identifies the user (via *\<saml:NameID>*).
  - Includes *\<saml:SubjectConfirmation>* elements for verification.
- **Statements**: There are three primary types of statements that can be included in an assertion:
  - `AuthenticationStatement`:
    - Details about when/how the user authenticated.
    - Includes details like timestamp, authentication method, and session index.
  - `AttributeStatement`:
    - Provide key-value pairs with user data (e.g., email, department, groups).
    - Used by the SP for access control and personalization.
  - `AuthorizationDecisionStatement`:
    - Specifies what the user can do (e.g., “Permit” for `Read` or `Write`).
    - Less common but useful for fine-grained authorization.
- **Conditions**:
  - Time-bound validity (`NotBefore`, `NotOnOrAfter`).
  - Audience restrictions ensuring only the intended SP can consume it.
- **Signature**:
  - XML Digital Signature that ensures integrity and authenticity. Typically uses RSA with SHA-256.

This is what a sample assertion looks like:

```clike
<saml:Assertion
    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
    xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
    ID="_abc123def456"
    Version="2.0"
    IssueInstant="2025-07-28T08:00:00Z">
    
    <!-- Issuer: The Identity Provider that created the assertion -->
    <saml:Issuer>https://idp.example.com</saml:Issuer>

    <!-- Digital signature ensures integrity and authenticity -->
    <ds:Signature>
        <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="#_abc123def456">
                <ds:Transforms>
                    <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                </ds:Transforms>
                <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
                <ds:DigestValue>ABCDEF1234567890...</ds:DigestValue>
            </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue>MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...</ds:SignatureValue>
        <ds:KeyInfo>
            <ds:X509Data>
                <ds:X509Certificate>MIIC+zCCAmOgAwIBAgIJ...</ds:X509Certificate>
            </ds:X509Data>
        </ds:KeyInfo>
    </ds:Signature>

    <!-- Conditions: Valid time window and audience restriction -->
    <saml:Conditions NotBefore="2025-07-28T08:00:00Z" NotOnOrAfter="2025-07-28T08:10:00Z">
        <saml:AudienceRestriction>
            <saml:Audience>https://app.example.com/saml/metadata</saml:Audience>
        </saml:AudienceRestriction>
    </saml:Conditions>

    <!-- Subject: Identifies the authenticated user -->
    <saml:Subject>
        <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">
            user@example.com
        </saml:NameID>
        <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
            <saml:SubjectConfirmationData 
                NotOnOrAfter="2025-07-28T08:10:00Z" 
                Recipient="https://app.example.com/saml/acs" />
        </saml:SubjectConfirmation>
    </saml:Subject>

    <!-- Authentication statement: When and how the user logged in -->
    <saml:AuthnStatement AuthnInstant="2025-07-28T08:00:00Z" SessionIndex="_session123">
        <saml:AuthnContext>
            <saml:AuthnContextClassRef>
                urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
            </saml:AuthnContextClassRef>
        </saml:AuthnContext>
    </saml:AuthnStatement>

    <!-- Attribute statement: User attributes like role, department, groups -->
    <saml:AttributeStatement>
        <saml:Attribute Name="Role" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue>Admin</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute Name="Department" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
            <saml:AttributeValue>Engineering</saml:AttributeValue>
        </saml:Attribute>
        <saml:Attribute Name="Groups">
            <saml:AttributeValue>DevOps</saml:AttributeValue>
            <saml:AttributeValue>CloudTeam</saml:AttributeValue>
        </saml:Attribute>
    </saml:AttributeStatement>

    <!-- Authorization decision: Indicates allowed actions -->
    <saml:AuthzDecisionStatement Resource="https://app.example.com/resource" Decision="Permit">
        <saml:Action Namespace="urn:oasis:names:tc:SAML:1.0:action:rwedc">Read</saml:Action>
        <saml:Action Namespace="urn:oasis:names:tc:SAML:1.0:action:rwedc">Write</saml:Action>
    </saml:AuthzDecisionStatement>
</saml:Assertion>
```

Below is a structured breakdown of the example SAML assertion:

| XML Tag / Field | Example Value | Explanation |
| --- | --- | --- |
| \<saml:Assertion ID> | _abc123def456 | Unique identifier for this assertion; prevents replay attacks. |
| \<saml:Issuer> | https://idp.example.com | Identity Provider that issued and signed the assertion. |
| \<ds:X509Certificate> | MIIC+zCCAmOgAwIBAgIJ... | Digital certificate used to verify the IdP’s signature. |
| \<saml:Conditions NotBefore> | 2025-07-28T08:00:00Z | Earliest time this assertion is valid. |
| \<saml:Conditions NotOnOrAfter> | 2025-07-28T08:10:00Z | Expiration time of this assertion. |
| \<saml:Audience> | https://app.example.com/saml/metadata | Specifies which Service Provider can consume this assertion. |
| \<saml:NameID> | user@example.com | Authenticated user’s unique identifier. |
| \<saml:AuthnStatement SessionIndex> | _session123 | Session identifier used by the SP for user session tracking. |
| \<saml:AuthnContextClassRef> | PasswordProtectedTransport | Indicates the method used for authentication (password over HTTPS). |
| \<saml:Attribute Name="Role"> | Admin | User’s role; can be used for access control. |
| \<saml:Attribute Name="Department"> | Engineering | User’s department information. |
| \<saml:Attribute Name="Groups"> | DevOps, CloudTeam | Groups or teams the user belongs to. |
| \<saml:AuthzDecisionStatement Decision> | Permit | Indicates the user has been granted permission. |
| \<saml:Action> | Read, Write | Lists specific actions the user can perform on the resource. |
| \<ds:Signature> | Signed with RSA-SHA256 | Ensures the assertion’s integrity and authenticity. |

## **What are SAML responses?**

While **SAML assertions** carry user identity and authorization information, a **SAML response** is the **message wrapper** that delivers assertions from the Identity Provider (IdP) to the Service Provider (SP).

- A **SAML response** is an XML document that contains one or more **assertions**.
- It’s signed by the IdP and sent to the SP’s **Assertion Consumer Service (ACS)** endpoint.
- A response can indicate either **success** (with assertions included) or **failure** (with error status codes).

## **What is the difference between a SAML assertion and a SAML response?**

A **SAML assertion** is the actual **security token** with authentication, attributes, and authorization decisions.

A **SAML response** is the **envelope** that contains assertions, along with status info.

Think of it like an **email (response)** that contains an **attachment (assertion)**.

## **Lifecycle of a SAML Assertion**

Understanding the detailed lifecycle of a SAML assertion is critical for building secure and reliable SSO implementations. Here’s what happens from creation to expiration:

### **1. Creation**

- A user authenticates with the Identity Provider (IdP).
- The IdP verifies credentials (e.g., username/password, MFA).
- IdP generates a signed SAML assertion containing user identity and attributes.
- A **unique Assertion ID** is created to avoid replay attacks.
- The assertion is signed with the IdP’s **private key** and includes:
  - `Issuer` information
  - `AuthnStatement` (method, timestamp, session index)
  - `AttributeStatement` (user metadata)
  - `AuthorizationDecisionStatement` (if applicable)
- **Optional encryption:** The assertion or its attributes can be encrypted with the Service Provider’s (SP) **public key** to ensure confidentiality.

### **2. Transportation**

- Assertion is encoded (Base64) and sent in a **SAML response** via HTTP POST or Redirect binding to the SP’s Assertion Consumer Service (ACS) endpoint.
  - TLS ensures confidentiality during transmission.
  - Parameters like `RelayState` maintain the user’s navigation context.
- This is typically done through the browser as part of the SSO handshake.

### **3. Validation**

- The SP receives the SAML response.
- Validation checks include:
  - **Signature verification:**
    - SP uses the IdP’s [**X.509 certificate**](https://workos.com/blog/x509-certificate) (public key) to verify the XML digital signature.
    - Ensures data hasn’t been tampered with.
  - **Conditions validation:**
    - `NotBefore` and `NotOnOrAfter` timestamps checked against SP’s system time.
    - Audience restriction ensures the assertion is intended for this SP.
  - **Subject confirmation:**
    - Bearer token confirmation (recipient matches SP ACS endpoint).
    - Optional InResponseTo verification for correlation with the SAML request.
- If all checks pass, the assertion is trusted.

### **4. Consumption**

- Upon successful validation, the SP establishes a session for the user.
  - SP generates a local session or issues its own session token (e.g., JWT).
  - The SAML assertion itself is **not reused** for subsequent requests.
- User attributes are mapped to application roles, permissions, and personalization. For example, Role=Admin enables admin dashboards.
- SP typically logs the Assertion ID and session index for future audit and troubleshooting.

### **5. Expiration and revocation**

- **Expiration** is defined by NotOnOrAfter in \<Conditions> and \<SubjectConfirmationData>. After this time, assertions are automatically rejected by SPs to prevent replay attacks.
- **Revocation:**
  - SAML itself does **not** support assertion revocation mid-lifecycle.
  - Revocation usually occurs via **session termination**:
    - IdP sends a **Single Logout (SLO)** message to the SP.
    - SP invalidates the session token (local logout).
- **Best Practices:**
  - Use **short assertion lifetimes** (e.g., 5–10 minutes).
  - Rely on IdP-initiated or SP-initiated logout for revoking access.
  - Implement continuous session checks for long-lived applications.

## **Common SAML assertion errors and how to fix them**

| Error Code / Message | Technical Cause | Resolution / Fix |
| --- | --- | --- |
| InvalidSignature | The SP could not verify the XML signature on the SAML response or assertion. | Ensure the IdP’s X.509 certificate is correctly configured on the SP side. Verify that the assertion was signed and not altered in transit. |
| AssertionExpired | The assertion’s NotOnOrAfter time has passed when the SP receives it. | Synchronize clocks between IdP and SP (NTP). Increase the assertion lifetime if network delays are expected. |
| AudienceMismatch | The AudienceRestriction element in the assertion doesn’t match the SP’s Entity ID. | Update the IdP’s configuration to include the correct SP Entity ID or check the SP metadata. |
| SubjectConfirmationError | The Recipient or InResponseTo attributes in SubjectConfirmationData do not match the SP’s ACS endpoint or request. | Ensure the ACS URL is correctly configured on both IdP and SP sides and that RelayState and request IDs align. |
| MissingAttribute | Required user attributes (e.g., NameID, Email, Role) are not present in the assertion. | Configure the IdP to include mandatory attributes in the AttributeStatement. Update attribute mappings if necessary. |
| InvalidRecipient | The Recipient attribute doesn’t match the SP’s Assertion Consumer Service (ACS) URL. | Correct the ACS endpoint configuration in the IdP settings to point to the proper SP endpoint. |
| SignatureAlgorithmMismatch | The SP expects a different XML signature algorithm (e.g., RSA-SHA256 vs RSA-SHA1). | Align the signature algorithm in the IdP configuration with what the SP expects. |

> For more, see [Diagnosing SAML assertion failures: A step-by-step debugging guide](https://workos.com/blog/saml-assertion-failures-debugging-guide).

## **Best practices for debugging SAML assertion errors**

- **Double-check metadata configuration:** Regularly verify entity IDs, endpoints, certificates, and bindings in both IdP and SP configurations.
- **Verify time synchronization:** Use NTP to sync clocks and ensure assertions meet time-based validity conditions.
- **Use SAML-tracer or browser extensions:** Capture and decode raw SAML requests/responses for analysis.
- **Implement robust error handling and logging:** Log detailed error messages, stack traces, and masked SAML content for easier debugging.
- **Validate assertion signatures:** Ensure certificates are valid and trusted; use XML signature validation tools.
- **Use online SAML validator tools:** Check XML structure, signature, and compliance with SAML standards.
- **Perform network traffic analysis:** Use Wireshark or tcpdump to detect TLS handshake failures or HTTP redirect issues.
- **Verify metadata consistency:** Confirm Entity IDs, ACS URLs, and bindings match between IdP and SP.
- **Test attribute mapping:** Ensure attributes like NameID, Email, and Role are correctly released and mapped.
- **Protect against replay attacks:** Log and reject reused Assertion IDs.
- **Use test environments:** Leverage WorkOS sandbox, Okta developer tenants, or staging IdPs to safely debug issues.

## **Implementing SAML SSO with WorkOS**

Implementing SAML SSO manually can be complex, but [**WorkOS**](https://workos.com/) simplifies it significantly:

**1. Sign up for WorkOS**

- [Sign up](https://signin.workos.com/sign-up) for a free WorkOS account.
- To make calls to WorkOS, you must authenticate using the WorkOS API key and client ID. Copy these values from the [WorkOS dashboard](https://dashboard.workos.com/).

![](https://cdn.prod.website-files.com/621f84dc15b5ed16dc85a18a/6889c5444f6c10a7253e85b5_image.webp)

**2. Configure an SSO connection**

- Add an [IdP connection](https://workos.com/docs/integrations) (Okta, Azure AD, Google Workspace, etc.).
- WorkOS handles fetching and managing SAML metadata automatically.

**3. Integrate the SDK**

- Use the [WorkOS SDK](https://workos.com/docs/sdks) in your app to handle authentication flows.
- You need to add a `login` endpoint and a `callback` endpoint.
- The `login` endpoint will be where users will be directed to sign in (or sign up). Example in [Node.js](http://node.js/):

```js
const express = require('express');
const { WorkOS } = require('@workos-inc/node');

const app = express();
const workos = new WorkOS(process.env.WORKOS_API_KEY, {
  clientId: process.env.WORKOS_CLIENT_ID,
});

// This `/login` endpoint should be registered as the login endpoint
// on the "Redirects" page of the WorkOS Dashboard.
app.get('/login', (req, res) => {
  const authorizationUrl = workos.userManagement.getAuthorizationUrl({
    // Specify that we'd like AuthKit to handle the authentication flow
    provider: 'authkit',

    // The callback endpoint that WorkOS will redirect to after a user authenticates
    redirectUri: 'http://localhost:3000/callback',
    clientId: process.env.WORKOS_CLIENT_ID,
  });

  // Redirect the user to the AuthKit sign-in page
  res.redirect(authorizationUrl);
});
```

- The `callback` endpoint will be where the user will be redirected after they have signed in. Example in Node.js:

```js
const express = require('express');
const { WorkOS } = require('@workos-inc/node');

const app = express();

const workos = new WorkOS(process.env.WORKOS_API_KEY, {
  clientId: process.env.WORKOS_CLIENT_ID,
});

app.get('/callback', async (req, res) => {
  // The authorization code returned by AuthKit
  const code = req.query.code;

  if (!code) {
    return res.status(400).send('No code provided');
  }

  const { user } = await workos.userManagement.authenticateWithCode({
    code,
    clientId: process.env.WORKOS_CLIENT_ID,
  });

  // Use the information in `user` for further business logic.

  // Redirect the user to the homepage
  return res.redirect('/');
});
```

**4. Validate the authentication flow**

Navigate to the authentication endpoint we created and sign up for an account. You can then sign in with the newly created credentials and see the user listed in [WorkOS dashboard > Users](https://dashboard.workos.com/environment/users).

![](https://cdn.prod.website-files.com/621f84dc15b5ed16dc85a18a/6889c54f262bcd5186c2da21_users.webp)

For more details, see the quickstart:

- If you use AuthKit for the UI, see [WorkOS AuthKit docs](https://workos.com/docs/authkit/vanilla/nodejs).
- If you use your own login box, see [WorkOS SSO docs](https://workos.com/docs/sso).

## **Final thoughts**

SAML assertions form the foundation of Single Sign-On integrations, securely carrying user identity and authorization data between Identity Providers and Service Providers. Understanding their structure, lifecycle, and the potential errors that can occur is essential for building robust authentication flows. With proper validation, debugging strategies, and adherence to best practices, developers can avoid common pitfalls and ensure seamless login experiences.

Modern tools like WorkOS further simplify SAML SSO implementation by handling complex protocol details behind the scenes. This allows engineering teams to focus on their applications while still supporting multiple identity providers with enterprise-grade security.
