skip to content

Metadata and Trust

The EntityDescriptor two parties exchange to declare endpoints, NameID formats and keys, and why validUntil and cacheDuration decide when trust goes stale. A stale key breaks every login at once.

part ofFederated identityoverview, primer and where to startread it →
on this pageshow

questions

5

In SAML 2.0, what does a party's `<EntityDescriptor>` metadata document declare, and what does exchanging it establish?

level: middleimportance: must knowfreq 55%

answer

  1. one document, one party
  2. entityID names software, not a person
  3. role descriptors hold the content
  4. keys, endpoints, NameID formats
  5. trust comes from the exchange itself

basics

~20 s

A SAML EntityDescriptor names one party by its entityID and declares, per role, the endpoints it exposes, the public keys it signs with or is encrypted to, and the NameID formats it supports. Two exchanged documents are the trust relationship.

solid answer

~40 s

A SAML 2.0 metadata document describes exactly one party. Its `<EntityDescriptor>` carries `entityID`, an absolute URI that names that party's deployed software — not a person — and is compared byte for byte rather than fetched. Beneath it sit role descriptors: `<IDPSSODescriptor>`, `<SPSSODescriptor>`, `<AttributeAuthorityDescriptor>`, or a generic `<RoleDescriptor>`, each carrying `protocolSupportEnumeration` to say which protocol it speaks. Inside a role descriptor are `<KeyDescriptor>` elements holding public keys, declared endpoints such as `<SingleSignOnService>`, `<AssertionConsumerService>`, `<SingleLogoutService>` and `<ArtifactResolutionService>`, and `<NameIDFormat>` elements listing supported subject-identifier formats. Nothing outside the exchanged pair vouches for any of it: in a bilateral relationship the trust comes from having accepted the counterparty's document out of band, which is why the certificates inside it are routinely self-signed.

code

xml · 20 lines
xml
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
                     entityID="https://portal.regulator.example/saml"
                     cacheDuration="PT12H">
  <md:SPSSODescriptor
      protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"
      WantAssertionsSigned="true">
    <md:KeyDescriptor use="encryption">
      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:X509Certificate>MIIC...</ds:X509Certificate>
      </ds:KeyInfo>
    </md:KeyDescriptor>
    <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>
    <md:AssertionConsumerService index="0" isDefault="true"
        Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
        Location="https://portal.regulator.example/saml/acs"/>
    <md:SingleLogoutService
        Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
        Location="https://portal.regulator.example/saml/slo"/>
  </md:SPSSODescriptor>
</md:EntityDescriptor>

go deeper

for a junior

Recall the shape: one XML document per party, naming that party and listing where to reach it and which public keys to believe. Know that the two parties swap documents before any login works.

for a middle

Explain the nesting: EntityDescriptor with entityID, then role descriptors carrying protocolSupportEnumeration, then key descriptors, declared endpoints with Binding and Location, and NameIDFormat entries. Say why endpoints are declared and never discovered.

for a senior

Show what the exchange costs to operate: every counterparty is a configuration act on both sides, the keys are trusted only because the document was accepted, and a missing or wrong endpoint is invisible until a real login hits it.

for a principal

Weigh bilateral exchange against the alternatives at estate scale: a few hundred pairwise relationships means a few hundred documents to keep current, with no operator above the pair to police any of it.

## What a SAML metadata document is A SAML 2.0 metadata document is an XML instance that describes **one party** to a federation relationship. For a single party the root element is `<EntityDescriptor>`, and its `entityID` attribute is that party's name: an absolute URI, chosen once, treated as an opaque string by everyone who reads it. Two properties of `entityID` catch people out: - It names **an organisation's deployed software**, not a human being. The identifier for a person travels inside an assertion, as a different element entirely. - It is conventionally written as an `https://` URI, but a counterparty **compares it, it does not dereference it**. The document may well be published somewhere else, or handed over as a file. Several `<EntityDescriptor>` elements may be wrapped in an `<EntitiesDescriptor>` container. In a strictly pairwise relationship that container rarely earns its keep; it exists for the case where one document carries many parties. ## Role descriptors: which hat the party wears An entity is described **per role**, and a role descriptor is what actually holds the useful content: - `<IDPSSODescriptor>` — the party in its identity-provider role. - `<SPSSODescriptor>` — the party in its service-provider role. - `<AttributeAuthorityDescriptor>` — the party answering attribute queries. - `<RoleDescriptor>` — the generic form, for a role the metadata schema does not define. One entity may carry more than one of these; a party that both consumes assertions from partners and issues them to others publishes both SSO role descriptors under a single `entityID`. Every role descriptor carries **`protocolSupportEnumeration`**, a whitespace-separated list of protocol URIs — `urn:oasis:names:tc:SAML:2.0:protocol` for SAML 2.0. When an entity publishes two role descriptors of the same type, that attribute is what selects between them. ## What sits inside a role descriptor | element | what it declares | shape | |---|---|---| | `<KeyDescriptor>` | a public key, with `use` drawn from `md:KeyTypes` (`signing`, `encryption`) | wraps `<ds:KeyInfo>`, usually a `<ds:X509Certificate>` | | `<SingleSignOnService>` | where the identity-provider role receives requests | `Binding` + `Location` | | `<AssertionConsumerService>` | where the service-provider role receives responses | indexed: adds `index`, `isDefault` | | `<SingleLogoutService>` | where logout messages are received | `Binding` + `Location` | | `<ArtifactResolutionService>` | where artifact resolution is answered | indexed: adds `index`, `isDefault` | | `<NameIDFormat>` | a subject-identifier format the role supports | a URI, repeatable | ## Endpoints are declared, not discovered There is no probing step in SAML. An endpoint the counterparty can use is an endpoint that appears in the document, with two attributes on it: **`Binding`**, holding the binding URI such as `urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST`, and **`Location`**, holding the URL. Indexed endpoints — `<AssertionConsumerService>` and `<ArtifactResolutionService>` — additionally carry **`index`**, a small integer that lets one endpoint be referred to by number rather than by URL, and **`isDefault`**, marking which is used when nothing selects. How messages are encoded onto those bindings, and which message goes where, are separate subjects; metadata only publishes the addresses. `<NameIDFormat>` elements are a capability statement in the same spirit: they say which subject-identifier formats this role can handle, for example `urn:oasis:names:tc:SAML:2.0:nameid-format:persistent`, `urn:oasis:names:tc:SAML:2.0:nameid-format:transient`, or `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress` — which keeps the older prefix. What such an identifier *means* once it appears in an assertion is the assertion's business, not the document's. ## Why exchanging two documents is the trust relationship In the bilateral case there is no third party vouching for anybody. A regulator's licensee reporting portal and one licensed operator's identity provider each publish one document; each side accepts the other's, out of band, once. From that moment: 1. The keys in the counterparty's `<KeyDescriptor>` elements are believed **because the document was accepted**, not because a certificate chains anywhere. This is why those certificates are commonly self-signed and given long lifetimes — they are key carriers. 2. The endpoints in it are the only addresses either side will use. 3. Adding a counterparty is a configuration act on **both** sides; so is removing one. Because the whole relationship rests on a cached copy of an XML file, the document must also say **when to stop believing it**. That is what `validUntil` and `cacheDuration` are for, and the root element of a metadata instance is required to carry at least one of them.

  • One party both issues assertions to partners and consumes them from others. How does its metadata express that?
    As one `<EntityDescriptor>` with a single `entityID`, carrying two role descriptors: an `<IDPSSODescriptor>` and an `<SPSSODescriptor>`. The entity is one party; the roles are what differ. A counterparty reads whichever role descriptor matches the relationship it has, selecting on `protocolSupportEnumeration` if more than one of a type appears.
  • What is the `index` attribute on an `<AssertionConsumerService>` for?
    It gives that endpoint a stable small-integer handle within the role descriptor, so a message can name the endpoint by number instead of repeating its URL. `isDefault="true"` marks the one used when nothing selects. It is unrelated to the `SessionIndex` value that names a session on a participant — same word, different mechanism.
  • Why are the certificates inside a `<KeyDescriptor>` so often self-signed?
    Because in a bilateral exchange the certificate is only a carrier for a public key. The reason a counterparty believes the key is that it arrived in the document that party accepted out of band, not that the certificate chains to a public trust anchor. That is also why they tend to be issued with very long lifetimes.

Two firms swapping signed letterheads and a list of postal addresses before they will accept each other's paperwork: nobody notarises the swap, the exchange itself is what makes each side's letterhead recognisable.

saying these in an interview costs you the question

  • Says entityID identifies the logged-in user rather than the party
  • Assumes a counterparty fetches the document from the entityID URI
  • Thinks the certificate in a KeyDescriptor must chain to a public CA
  • Believes endpoints can be discovered or probed instead of declared
  • Treats the document as describing both parties rather than one
open as a page

In a SAML metadata document, what do `validUntil` and `cacheDuration` each tell the party consuming it?

level: middleimportance: must knowfreq 50%

basics

~20 s

validUntil is an absolute instant after which the element and its contents must not be used at all. cacheDuration is a relative ceiling on how long a consumer may keep a copy before refetching. They answer different questions and a root element needs at least one.

open as a page

In a SAML role descriptor, what does the `use` attribute on a `<KeyDescriptor>` select, and what does omitting it mean?

level: seniorimportance: should knowfreq 38%

basics

~20 s

The use attribute is drawn from md:KeyTypes and takes the values signing or encryption, saying which purpose that published key serves. It is optional, and when omitted the key may be used for both. A role may publish several key descriptors.

open as a page

In SAML metadata, what do `WantAuthnRequestsSigned`, `AuthnRequestsSigned` and `WantAssertionsSigned` declare — and what are they not?

level: seniorimportance: should knowfreq 30%

basics

~20 s

They are optional boolean declarations: the identity-provider role's WantAuthnRequestsSigned asks for signed requests, while the service-provider role's AuthnRequestsSigned says it signs its own and WantAssertionsSigned asks for signed assertions. They state expectations; they enforce nothing.

open as a page

With SAML metadata as your only lever, how do you move a signing key across several hundred counterparties you cannot compel?

level: principalimportance: should knowfreq 22%

basics

~20 s

Publish the incoming key as a second KeyDescriptor while the outgoing one is still live, wait out the cacheDuration in the copies counterparties already hold, then switch and later withdraw the old one. The clock is set by the old duration, not the new.

open as a page