What it means
A SAML assertion carries an AudienceRestriction naming who it was issued for.
The service provider compares that value against its own entity ID and rejects
anything that does not match exactly.
The signature can be perfectly valid and the certificate perfectly current. This is an addressing problem, not a trust problem.
The comparison that matters
Two strings must be byte-identical:
<saml:Audience>inside the assertion the IdP issued- The service provider’s own configured entity ID (also called Identifier, Audience URI, or SP Entity ID depending on the product)
Everything below is a reason those two strings differ.
Causes, in the order they actually occur
1. Trailing slash.
https://app.example.com/saml and https://app.example.com/saml/ are different
audiences. This is the single most common cause and the easiest to overlook,
because the two render identically in most user interfaces.
2. http versus https. Entity IDs are opaque identifiers, not addresses. Changing the scheme changes the identifier even when both URLs serve the same page.
3. Copied configuration between environments. A staging connection cloned to production keeps the staging audience. Logins fail only in production, which is where they matter.
4. The SP changed its entity ID. A rename, a domain migration, or a platform upgrade changed the identifier and the IdP-side configuration still carries the old one.
5. Multiple applications sharing one IdP connection. Each service provider needs its own audience. One connection issuing assertions for two SPs will fail for at least one of them.
Confirming it
Capture the assertion from a failing sign-in — a browser network trace with
“preserve log” enabled captures the SAMLResponse form field; base64-decode it
and read the <saml:Audience> element. Compare that value character by character
with what the SP has configured. Paste both into a diff rather than eyeballing
them; trailing whitespace and slashes are exactly what the eye skips.
Reading the IdP metadata shows the entity ID the IdP publishes for itself, which is the other identifier in play and is often the one confused with the audience.
Fixing it
Change whichever side is wrong so the strings match exactly. There is no normalisation step in the protocol and no partial match: entity IDs are compared literally.
If you control both sides, prefer changing the IdP-side audience — service provider entity IDs are frequently baked into other integrations and are the more expensive value to move.