skip to content

In a SAML assertion, what does the `<Conditions>` `NotOnOrAfter` bound that `<SubjectConfirmationData>` `NotOnOrAfter` does not?

level: middleimportance: must knowfreq 58%

answer

  1. two windows, not one expiry
  2. one bounds the statement, one the delivery
  3. NotOnOrAfter excludes the named instant
  4. bearer confirmation data carries no NotBefore
  5. Recipient names the endpoint that received it

basics

~20 s

The Conditions NotOnOrAfter bounds how long the assertion is a valid statement at all; the SubjectConfirmationData NotOnOrAfter bounds only how long this bearer may present it for delivery. They are two separate clocks and both must hold.

solid answer

~40 s

A SAML assertion carries two independent time windows. `<Conditions NotBefore=... NotOnOrAfter=...>` bounds the statement itself: outside that window the identity provider is not asserting anything a relying party may act on. The `NotOnOrAfter` on `<SubjectConfirmationData>` bounds something narrower — how long the holder may present this copy to confirm the subject — and in the Web Browser SSO profile it is usually a few minutes, since the only thing that has to happen inside it is one browser hop. That profile also requires a bearer `<SubjectConfirmationData>` to carry a `Recipient` and a `NotOnOrAfter` and forbids a `NotBefore` on it. Both instants are absolute UTC times, `NotOnOrAfter` excludes the named instant, and an assertion is only usable when both windows hold.

code

xml · 19 lines
xml
<saml:Subject>
  <saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">
    9c41e0b7-driver
  </saml:NameID>
  <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
    <!-- delivery window: this holder, this endpoint, next five minutes -->
    <saml:SubjectConfirmationData Recipient="https://intake.coop.example/acs"
                                  NotOnOrAfter="2026-09-19T08:19:02Z"
                                  InResponseTo="_req41ab"/>
  </saml:SubjectConfirmation>
</saml:Subject>

<!-- validity window: how long this is a statement at all -->
<saml:Conditions NotBefore="2026-09-19T08:13:52Z"
                 NotOnOrAfter="2026-09-19T08:24:02Z">
  <saml:AudienceRestriction>
    <saml:Audience>https://intake.coop.example/sp</saml:Audience>
  </saml:AudienceRestriction>
</saml:Conditions>

go deeper

for a junior

Remember that an assertion carries more than one time limit, and that a time written as NotOnOrAfter has already lapsed at the instant it names.

for a middle

Explain which element bounds the statement and which bounds the carrier, and why the profile requires Recipient and NotOnOrAfter on a bearer confirmation but forbids NotBefore.

for a senior

Recognise the skewed-clock signature — intermittent failures at one site, worst right after issue — and be able to say which of the two windows is being crossed and by how much.

for a principal

Decide what window lengths you will accept from counterparties you cannot audit, knowing a generous validity window is a liability you are agreeing to on their behalf.

## Two windows, not one The most common misreading of a SAML assertion is that it has *an* expiry. It has at least two, on different elements, bounding different things, and a relying party has to satisfy both. At the co-operative's grain intake, the difference is easy to see. The haulier's identity provider signs a statement about the driver at 08:14. The browser must carry that document from the identity provider to the intake system's consuming endpoint — a matter of seconds. But the intake clerk may still be looking at the resulting screen ten minutes later. The first of those spans is what `<SubjectConfirmationData>` bounds; the second is nearer what `<Conditions>` bounds. ## What each one says - **`<Conditions>` `NotBefore` / `NotOnOrAfter`** — the window inside which this is a statement at all. Before `NotBefore` the identity provider has not yet vouched for anything; at or after `NotOnOrAfter` it no longer does. This is a property of the **statement**. - **`<SubjectConfirmationData>` `NotOnOrAfter`** — the last instant at which *this holder* may present *this copy* to confirm that they are the subject. This is a property of the **delivery**, and it belongs to the confirmation method, not to the statement's truth. Both are absolute instants in UTC, and both use the same exclusive convention: `NotOnOrAfter="2026-09-19T08:19:02Z"` means the last usable moment is strictly before 08:19:02, not up to and including it. `NotBefore` is inclusive — the named instant already counts. | Attribute | On which element | What it bounds | Typical span | |---|---|---|---| | `NotBefore` | `<Conditions>` | when the statement starts being one | issue time, or slightly before | | `NotOnOrAfter` | `<Conditions>` | when the statement stops being one | minutes, a deployment choice | | `NotOnOrAfter` | `<SubjectConfirmationData>` | how long this holder may deliver this copy | a few minutes at most | | `Recipient` | `<SubjectConfirmationData>` | the endpoint this copy was meant for | one absolute URL | ## The profile rules that go with them The core assertion grammar defines the elements; the Web Browser SSO profile adds the rules that matter in practice for a bearer subject confirmation: 1. The `<SubjectConfirmationData>` **must** carry a `Recipient` naming the endpoint the assertion was delivered to. 2. It **must** carry a `NotOnOrAfter`. 3. It **must not** carry a `NotBefore`. There is nothing to bound at the lower end: the holder cannot deliver a document that does not yet exist, and a lower bound on delivery only creates a window in which a correct message is rejected. 4. It **must** carry `InResponseTo` when the assertion answers an authentication request. What that value is matched against is a question about the flow, not about the assertion's structure; the attribute itself simply names the request this confirmation answers. ## Why a short delivery window is not enough on its own A reader sometimes concludes that if the delivery window is two minutes, the `<Conditions>` window hardly matters. It matters for a different reason: the `<Conditions>` window is the one that says how long the *content* stands. A statement that a driver authenticated is not made permanent by being delivered promptly, and a relying party that keeps the document and reads it again later is outside the window even though delivery was instant. The reverse mistake is worse. If a relying party checks only the `<Conditions>` window — commonly the longer of the two — then a copy of the document that leaked out of the browser hop remains presentable for the whole of that longer window. ## Clocks, and what the document cannot say Both windows are written as absolute UTC instants by the issuer and evaluated against the reader's own clock. Nothing in the assertion expresses a tolerance: there is no skew attribute, no relative lifetime, no "seconds from receipt". If the two parties' clocks disagree by more than the delivery window, a perfectly good assertion arrives already outside it — which is why the classic symptom of a skewed clock in this protocol is an intermittent login failure at one site and nowhere else, worst around the moment of issue. ## What to take away - Two windows, checked independently; failing either means the assertion cannot be used. - `NotOnOrAfter` excludes its own instant. - The narrow one bounds the **carrier**; the wide one bounds the **statement**. - Neither window says anything about how long the intake system's own session may last — that is the intake system's decision.

  • Why does the Web Browser SSO profile forbid a `NotBefore` on a bearer `<SubjectConfirmationData>`?
    Because there is nothing to bound at the lower end. The holder cannot present a document before the issuer produced it, so a lower bound adds no protection and only creates a window in which a correct, promptly delivered assertion is refused because the two clocks disagree by a second or two.
  • What does `Recipient` add that `<Audience>` does not?
    They constrain different things. `<Audience>` names the **party** the statement is addressed to, as an `entityID` URI. `Recipient` names the **endpoint URL** this particular copy was delivered to. One party may expose more than one consuming endpoint, so a copy that is audience-correct can still have been handed to the wrong one.
  • How does a SAML assertion express an allowance for clock skew between the two parties?
    It does not. Every instant in the document is an absolute UTC time written by the issuer, and there is no skew attribute, no relative lifetime and no 'seconds from receipt'. Any tolerance is the reader's own policy, applied outside the document, which is why a mis-set clock at one site produces failures nowhere else.

saying these in an interview costs you the question

  • Says the assertion has one expiry, the Conditions NotOnOrAfter.
  • Reads NotOnOrAfter as valid up to and including that instant.
  • Thinks a bearer SubjectConfirmationData may carry a NotBefore.
  • Treats the short delivery window as the local session lifetime.
  • Assumes matching either window is enough to accept the assertion.
  • Expects a skew tolerance to be written inside the assertion.