SAML is how enterprise applications stop making you log in a hundred times a day, by letting one login credential unlock them all.

Let’s see it in action. Imagine you’re an employee at "AwesomeCorp" and you need to access your HR portal and your company’s expense reporting tool. Both are separate applications, but AwesomeCorp has set up SAML.

First, you go to the AwesomeCorp Identity Provider (IdP) portal, which is usually a simple login page. You enter your corporate username and password. This is your one login.

Once authenticated by the IdP, it generates a SAML assertion. This assertion is an XML document containing information about you – your identity, your roles, group memberships, etc. Crucially, this XML document is digitally signed by the IdP using its private key. Think of this signature as a tamper-proof seal.

The IdP then redirects your browser to the first application, say, the HR portal. It passes this signed SAML assertion along. The HR portal (which is a Service Provider, or SP) receives the assertion. It doesn’t know your password; it never sees it. Instead, it uses the IdP’s public key (which it was configured with beforehand) to verify the digital signature on the assertion. If the signature is valid, the HR portal trusts that the IdP has authenticated you. It then grants you access based on the information within the assertion.

The same process happens when you navigate to the expense reporting tool. It also receives the SAML assertion, verifies its signature with the IdP’s public key, and grants you access. You didn’t have to log in again.

The magic here is the trust relationship established between the IdP and the SPs. The IdP vouches for your identity, and the SPs trust the IdP’s vouching. This is achieved through public-key cryptography and XML signatures.

Here’s a simplified look at a SAML assertion (it’s much more verbose in reality):

<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_someuniqueid" Version="2.0" IssueInstant="2023-10-27T10:00:00Z">
  <saml:Issuer>https://awesomecorp.com/idp</saml:Issuer>
  <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <!-- This is where the digital signature data goes -->
    <ds:SignedInfo>
      <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
      <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
      <ds:Reference URI="#_someuniqueid">
        <ds:Transforms>
          <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
          <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
        </ds:Transforms>
        <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
        <ds:DigestValue>...base64encodeddigest...</ds:DigestValue>
      </ds:Reference>
    </ds:SignedInfo>
    <ds:SignatureValue>...base64encodedvalue...</ds:SignatureValue>
    <ds:KeyInfo>
      <ds:X509Data>
        <ds:X509Certificate>...idp_public_certificate...</ds:X509Certificate>
      </ds:X509Data>
    </ds:KeyInfo>
  </ds:Signature>
  <saml:Subject>
    <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">jane.doe@awesomecorp.com</saml:NameID>
    <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
      <saml:SubjectConfirmationData NotOnOrAfter="2023-10-27T10:05:00Z" Recipient="https://hr.awesomecorp.com/saml/consume"/>
    </saml:SubjectConfirmation>
  </saml:Subject>
  <saml:Conditions NotBefore="2023-10-27T09:59:00Z" NotOnOrAfter="2023-10-27T10:05:00Z">
    <saml:AudienceRestriction>
      <saml:Audience>https://hr.awesomecorp.com/saml/metadata</saml:Audience>
    </saml:AudienceRestriction>
  </saml:Conditions>
  <saml:AttributeStatement>
    <saml:Attribute Name="email">
      <saml:AttributeValue>jane.doe@awesomecorp.com</saml:AttributeValue>
    </saml:Attribute>
    <saml:Attribute Name="groups">
      <saml:AttributeValue>employees</saml:AttributeValue>
      <saml:AttributeValue>hr-access</saml:AttributeValue>
    </saml:Attribute>
  </saml:AttributeStatement>
</saml:Assertion>

The <ds:Signature> block is the critical part for security. It contains the ds:SignatureValue which is the actual encrypted hash of the assertion’s content, and ds:KeyInfo which typically holds the IdP’s public certificate (ds:X509Certificate). The SP uses this certificate to decrypt the signature, recalculate the hash of the assertion, and compare it. If they match, the assertion is authentic and unaltered. The <saml:Conditions> element specifies when the assertion is valid, and <saml:Audience> ensures it’s meant for the specific SP. <saml:AttributeStatement> carries the user’s attributes.

The core problem SAML solves is federated identity – allowing different security domains (your company and a SaaS provider) to trust each other regarding user authentication. Without it, each SaaS application would need to manage its own user accounts and passwords, creating a security and usability nightmare. SAML enables a "trust fabric" where an identity provider acts as the central authority.

The part most people miss is how the XML Signature is technically verified. It’s not just a simple check; it involves canonicalizing the XML (ensuring consistent formatting so whitespace or attribute order doesn’t break the hash), calculating a digest of the canonicalized XML using a specified algorithm (like SHA-1), and then using the IdP’s public key to decrypt that digest. The SP then performs the same canonicalization and digest calculation on the received assertion and compares it to the decrypted digest. If they match, the assertion is valid.

The next hurdle is understanding SAML’s role in authorization, not just authentication.

Want structured learning?

Take the full Cryptography course →