In this article
July 30, 2025
July 30, 2025

What are SAML assertions?

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.

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) and a Service Provider (SP).

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) 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:

  
<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 (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.!!

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 simplifies it significantly:

1. Sign up for WorkOS

  • 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.

2. Configure an SSO connection

  • Add an IdP connection (Okta, Azure AD, Google Workspace, etc.).
  • WorkOS handles fetching and managing SAML metadata automatically.

3. Integrate the SDK

  • Use the WorkOS SDK 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:
	
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:
	
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.

For more details, see the quickstart:

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.

This site uses cookies to improve your experience. Please accept the use of cookies on this site. You can review our cookie policy here and our privacy policy here. If you choose to refuse, functionality of this site will be limited.