In a SAML 2.0 Response message, what does the InResponseTo attribute prove, and what must a service provider compare it against?
answer
- the answer quotes the question
- the service provider generated the value
- look it up in your own pending state
- a miss is a rejection, not a fallback
- correlation only, not authenticity
basics
~20 sInResponseTo carries the ID of the AuthnRequest a response answers. The service provider must compare it against a request ID it generated and is still awaiting, and reject any response whose value it cannot find.
solid answer
~50 s`InResponseTo` on a `<samlp:Response>` holds the `ID` of the `<samlp:AuthnRequest>` the response answers, and the specification requires it to match that value. The service provider generates that `ID`, stores it with the pending login before the browser leaves, and on receipt looks the arriving value up in its own state. A hit proves one thing precisely: **this response answers a request I issued and am still waiting for**. It does not prove who the person is — the assertion does that — and it does not prove the message is authentic, which is the signature's job. A miss must be a rejection, not a fallback to treating the login as unsolicited, because that fallback silently disables the check. The stored entry is consumed on use, so the same answer cannot be presented twice into the same pending slot.
code
xml · 9 lines<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="_a4f1c9e2b7d3"
Version="2.0"
IssueInstant="2026-09-19T09:14:05Z"
Destination="https://idp.university.example/sso">
<saml:Issuer>https://loans.example/sp</saml:Issuer>
</samlp:AuthnRequest>go deeper
Remember that the value in the response is the identifier of the request that caused it, and that the service provider made that identifier up in the first place.
Explain the full loop: generate, store bound to the browser, redirect, look up on return, consume, reject on a miss — and say what a match does and does not establish.
Show the failure you have actually seen: the miss handled as an unsolicited login, the predictable identifier, the pending store with no expiry, the mismatch logged and then ignored.
Decide how much correlation your integration requires of partners at all, knowing that permitting unsolicited logins removes this check by design rather than by bug.
## The attribute `InResponseTo` is an optional attribute of `<samlp:Response>`. When the response was produced in answer to a request, it carries that request's `ID` verbatim; when there was no request, it MUST NOT be present. That is the whole of the wire rule, and everything interesting is what the receiving service provider is supposed to do with it. The `ID` itself is the service provider's own invention. Before the browser leaves for the identity provider, the service provider generates an identifier that is unique and unguessable, puts it in the `<AuthnRequest>`, and stores it in state tied to that browser along with anything else it will need when the reader comes back — typically the resource the reader was reaching for. ## The check, in order 1. A `<Response>` arrives at the assertion-consumer endpoint. 2. If `InResponseTo` is absent, the login is unsolicited; apply the unsolicited policy rather than the matching rule. 3. If it is present, look the value up in the service provider's pending-request state. 4. No entry — reject. The response answers a request this service provider never made, or one it has already consumed, or one that has expired. 5. An entry — consume it, so the same value cannot be presented again, and carry on with validating the assertion and its signature. Step 4 is where integrations go wrong. "Not found, so treat it as an unsolicited login" is an easy line of code to write and it turns the check off completely: every response that fails the match is simply promoted into the path that has no match at all. ## What a match proves — and what it does not | Question | Does a matching `InResponseTo` answer it? | |---|---| | Is this response an answer to a request I issued? | **Yes** — that is exactly what it establishes | | Am I still expecting an answer to that request? | **Yes**, if the stored entry is still present and unconsumed | | Did the identity provider really produce this message? | No — that is the signature over the message or assertion | | Who is the person, and for how long is the statement valid? | No — that is the assertion, its subject and its validity conditions | | Is this the browser that started the login? | Only as far as the service provider's own request state is bound to that browser | The distinction matters because a weak candidate treats the attribute as a security control on its own. It is not one: it is a **correlation** control. Its value is that the service provider can insist on having asked a question before it will accept an answer, which is precisely what an integration loses when it also accepts unsolicited responses. ## What the correlation buys you in practice The inter-library loan service sends a reader off to their institution with a request whose `ID` it filed next to "this reader was trying to borrow volume 4718". Three things follow from the match when they come back: - **The answer is attached to the question.** A response produced for some other login attempt, captured or replayed, does not match an outstanding entry and is rejected before any assertion processing happens. - **The state survives the detour.** The deep link, the attribute set requested, the authentication strength asked for: all of it was filed under that `ID`, and the match is what retrieves it. - **Stale answers die.** The stored entry has a lifetime of the service provider's choosing. A response that turns up long after the reader gave up finds nothing to match. ## Common failure modes - **Storing the request identifier where it is not scoped to one browser.** If the pending-request store is global, any browser can complete any login. - **Predictable identifiers.** A sequential counter lets a third party name a request identifier in advance. The value should be random and unguessable. - **Never expiring the entry.** Pending logins that live forever turn the store into a growing liability and keep stale answers acceptable indefinitely. - **Comparing loosely.** The comparison is over the exact string the service provider generated; trimming, case-folding or substring matching all weaken it for no benefit. - **Checking it and then ignoring the result.** Logging a mismatch and continuing is the same as not checking. An interviewer asking this is usually checking one reflex: whether the candidate knows that the service provider generated the value being compared. If the answer is "we compare it with the identity provider's metadata" or "with the assertion's own identifier", the candidate has never stored a pending request, and probably runs an integration that accepts anything the identity provider sends.
- Where should the service provider keep the request ID while the reader is away at the identity provider?In state bound to that one browser and to nothing else — server-side state keyed by the browser's session, or a value the service provider can bind back to the browser. A globally readable store lets any browser complete any pending login, which removes the correlation the check exists to provide.
- Why must the stored request identifier be unguessable rather than merely unique?Because a third party who can predict the next identifier can pre-build or solicit a response that will match an entry the service provider is about to create. Uniqueness stops collisions; unpredictability is what stops someone naming your request before you do.
- What should happen when the arriving value matches nothing?Reject the login and start over. The tempting fallback — treat it as unsolicited — quietly routes every failed match into the path with no match at all, so the check stops rejecting anything. If unsolicited logins are permitted at all, that decision belongs to responses with no `InResponseTo`, not to responses whose value failed to resolve.
It is the reference number an office writes on a reply so the clerk can find the file the letter belongs to. A letter with no reference is not proof of anything — it is an unprompted delivery, and the office has to decide whether it accepts those at all.
saying these in an interview costs you the question
- Says a matching value proves the person authenticated just now
- Treats a lookup miss as an unsolicited login
- Thinks the identity provider generates the request ID
- Compares the value against the identity provider's metadata
- Keeps pending request state that never expires
- Believes the attribute replaces verifying the signature