SAML Bindings, Metadata, Setup, and Single Logout
Part 3 of the SAML From First Principles series
We know what SAML messages contain and how the flows work. But how do these messages physically travel between the IdP and the SP? How is trust configured? And what does setup actually look like?
SAML Bindings
You've written a letter (the SAML message). Now, do you hand-deliver it, mail it, or send it via courier? Each delivery method is a "binding."
HTTP Redirect Binding. The SAML message is compressed and Base64-encoded (converted into plain text characters so it can travel safely in a URL), then stuffed into the URL query string of a 302 redirect. Used for small messages like the AuthnRequest. The limitation is URL length (about 2000 characters), so it can't carry full assertions.
HTTP POST Binding. The SAML message is Base64-encoded and placed in a hidden form field inside an auto-submitting HTML form:
<form method="POST" action="https://app.com/acs">
<input type="hidden" name="SAMLResponse" value="PHNhbWw..." />
<input type="hidden" name="RelayState" value="https://app.com/dashboard" />
</form>
<script>document.forms[0].submit();</script>
The IdP returns this HTML page to the browser, and JavaScript automatically submits the form to the SP's ACS URL. Used for the SAML Response because assertions are large XML documents that don't fit in a URL.
Artifact Binding. Instead of sending the full assertion through the browser, the IdP sends a small reference token (an "artifact") to the browser. The SP then takes that artifact and makes a direct server-to-server call to the IdP to fetch the actual assertion. This is more secure because the assertion never passes through the user's browser, where it could potentially be intercepted or tampered with. It's less common because it requires the SP and IdP to have direct network connectivity to each other.
Think of it this way: instead of mailing you the actual diamond (risky, someone could steal it in transit), I mail you a claim ticket. You take the ticket directly to the vault to pick up the diamond yourself.
The typical combination: AuthnRequest uses HTTP Redirect (small, fits in URL). SAML Response uses HTTP POST (large, needs form body).
SAML Metadata
Setting up trust between an IdP and an SP requires exchanging a lot of information. The SP needs the IdP's SSO URL, signing certificate, and supported bindings. The IdP needs the SP's ACS URL and entity ID. Doing this manually is tedious and error-prone.
Metadata solves this. It's an XML document that describes everything about an IdP or SP. Think of it as a business card. When two companies want to do business, they exchange business cards with their address, phone number, and services offered.
IdP Metadata contains:
- Entity ID (a unique identifier for this IdP, usually a URL like
https://login.okta.com/abc123) - SSO URL (where to send authentication requests)
- SLO URL (where to send logout requests)
- Signing certificate (public key for verifying signatures)
- Supported NameID formats and bindings
SP Metadata contains:
- Entity ID (a unique identifier for this SP, like
https://slack.com/saml) - ACS URL (where the SP receives assertions)
- SLO URL
- Encryption certificate
- Required attributes
How it's exchanged: Most IdPs publish a metadata URL. The SP imports it, and all configuration is set up automatically. That's why setup wizards often just ask for a "metadata URL." You paste it in, the SP fetches the XML, parses out the SSO URL, certificate, and bindings, and configures itself. No manual copying of URLs and certificates.
Metadata also helps with certificate rollover. When the IdP rotates its signing certificate, the metadata can be refreshed to pick up the new certificate automatically.
NameID Formats
When the IdP tells the SP "this user is authenticated," it needs to identify who the user is. The NameID is that identifier. It comes in different formats depending on the use case.
| Format | Example | Use case |
|---|---|---|
| Email Address | alice@company.com | Most common, human-readable, easy to match to an account |
| Persistent | _a83nf82k... | Same opaque ID every time the user logs in, but not human-readable. Used when you don't want the SP to know the user's email. |
| Transient | _x7km29p... | Different random ID each session. Maximum privacy. The SP can't track the user across sessions. |
| Unspecified | Whatever the IdP decides | Flexible, IdP picks the format |
| Windows Domain | DOMAIN\alice | Active Directory environments |
For most enterprise applications, Email Address is the right choice. The SP needs to match the incoming user to an account, and email is the simplest way to do that. The format is agreed upon during configuration.
Persistent and Transient exist for privacy. If you don't want Salesforce to know that the same person also uses your HR system, you give each SP a different opaque identifier. They can't correlate users across services because each one sees a different ID for the same person.
Setting Up SAML in Practice
Regardless of which IdP or SP you're using, the setup follows the same pattern. The admin interfaces look different (Okta looks different from Azure AD looks different from Ping Identity), but the information being exchanged is identical.
What the IdP needs about the SP:
- ACS URL (where to send the assertion)
- Entity ID (the SP's unique identifier)
- NameID format (usually email)
What the SP needs about the IdP:
- SSO URL (where to redirect users for login)
- Entity ID
- Signing certificate (to verify assertion signatures)
The exchange: Import metadata on both sides (or manually enter the URLs and certificates). Assign users or groups to the application on the IdP side. Configure which attributes to send in the assertion (role, department, groups).
Common setup issues:
- Mismatched ACS URL (even a trailing slash breaks it —
https://app.com/acsvshttps://app.com/acs/) - Wrong or expired signing certificate
- Clock skew between IdP and SP (the SP thinks the assertion is expired because its clock is 6 minutes ahead)
- Missing attributes the SP expects (SP wants a "role" attribute but the IdP isn't sending one)
Every SAML integration you'll ever do follows this same pattern. Once you've done it once, you can do it for any IdP and any SP.
Single Logout (SLO)
SSO lets you log in once and access everything. But what about logging out?
Without Single Logout, if you log out of Slack, you're still logged into Jira and Gmail. Your sessions at those applications are still active. Someone who sits down at your computer after you "logged out" of Slack could still access your Jira and Gmail.
How SLO should work:
- You click logout in Slack
- Slack sends a LogoutRequest to the IdP
- The IdP sends a LogoutRequest to every other SP where you have an active session (Jira, Gmail, Salesforce)
- Each SP clears your session and responds
- The IdP clears its own session
In theory, one logout button logs you out of everything. In practice, it's fragile.
Why SLO is unreliable in practice:
- If one SP is down or slow, the logout chain breaks. The IdP is waiting for a response that never comes.
- Some SPs don't implement SLO at all. They just ignore the logout request.
- Browser-based SLO requires visiting each SP's logout endpoint in sequence (loading hidden iframes or redirecting). If the user closes the browser mid-chain, remaining SPs keep the session.
- Network issues between the IdP and any SP break the chain.
What organizations do instead: Short session timeouts (15-30 minutes of inactivity). When the IdP session expires, the next time any SP redirects to the IdP for authentication, the user has to log in again. It's not true single logout, but it's reliable and limits the exposure window. Most security teams accept this tradeoff.
Previous: How SAML Works — Flows, SSO, and the Assertion
Next: SAML Security Pitfalls
← Back to all posts