skip to content

Single Logout

Ending a federated session everywhere at once: LogoutRequest and LogoutResponse, SessionIndex, and front-channel chaining against back-channel SOAP. Asked because it half-works in production.

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

questions

4

In a SAML `<LogoutRequest>`, what does a `SessionIndex` child add that the `<NameID>` alone does not?

level: middleimportance: must knowfreq 57%

answer

  1. the principal, then which of their sessions
  2. one person can be signed in twice
  3. zero or more children, not exactly one
  4. omit it and the recipient must guess
  5. the identity provider chose the value

basics

~20 s

The identifier names the principal; SessionIndex names one particular session that recipient holds for them. Without it a recipient cannot tell which of a person's several live sessions to end, so it must end all of them or guess.

solid answer

~40 s

A `<LogoutRequest>` carries exactly one identifier for the principal — `<saml:BaseID>`, `<saml:NameID>` or `<saml:EncryptedID>` — in a form the recipient currently recognises, and zero or more `<samlp:SessionIndex>` children. The identifier answers *whose* session, the `SessionIndex` answers *which one*. That matters because one person is routinely signed in more than once: a laptop and a tablet, or two browsers. Each login produced its own session at the participant, and the identity provider supplied a distinct index value for each. Send only the identifier and the recipient has to end everything it holds for that person, or guess; send the index and it ends exactly the session that is going away. A participant that was never given an index for a session has the same problem in reverse — it has nothing to match on.

code

xml · 15 lines
xml
<samlp:LogoutRequest
    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
    ID="_8f2c1a63" Version="2.0"
    IssueInstant="2026-09-19T10:04:12Z"
    Destination="https://idp.example.org/slo"
    NotOnOrAfter="2026-09-19T10:09:12Z"
    Reason="urn:oasis:names:tc:SAML:2.0:logout:user">
  <saml:Issuer>https://review.example.com/sp</saml:Issuer>
  <saml:NameID
      Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">
    d7f1e40c9ac2
  </saml:NameID>
  <samlp:SessionIndex>_5b31e0c4</samlp:SessionIndex>
</samlp:LogoutRequest>

go deeper

for a junior

Recall that the message carries two different things: a name for the person and a name for one of their logins. They are not interchangeable.

for a middle

Explain the mechanics: exactly one identifier, zero or more index children, where the index value originated, and what a recipient does when it is absent.

for a senior

Demonstrate the storage discipline that makes this work — keeping the identifier and index with the session record, and deciding deliberately what an unmatched index does.

for a principal

The judgment call is default blast radius: ending every session for an unmatched principal buys certainty and costs working users, and that default belongs in a written policy.

## Two different questions in one message A `<LogoutRequest>` has to answer two questions before a recipient can act on it, and they are answered by different children. | child element | what it names | how many | |---|---|---| | `<saml:BaseID>`, `<saml:NameID>` or `<saml:EncryptedID>` | the principal, in a form the recipient recognises | exactly one | | `<samlp:SessionIndex>` | one session the recipient holds for that principal | zero or more | The identifier is a name for a human. The index is a name for an episode. Conflating them is the single most common misreading of this message, and it produces two opposite production bugs: ending sessions that should have survived, and ending none at all. ## Why one principal has several sessions Nothing in federation limits a person to one login. Outside counsel reviewing documents for an e-discovery engagement signs in from a laptop in the morning and from a tablet after lunch. Each sign-in ran its own exchange and produced its own session at the review platform, and the identity provider handed out a distinct session index value for each. When the tablet's session is being ended — the device was handed back, or the user pressed sign out there — the laptop session must survive. Only the index distinguishes them. ## What the recipient does with each field 1. Resolve the identifier to a local principal. If the format is a per-login transient identifier, the recipient must have stored the exact value it received, because a later login produces a different one. 2. Look up the sessions it holds for that principal. 3. If one or more `<samlp:SessionIndex>` children are present, end the sessions those values index and leave the rest alone. 4. If none is present, the specification does not dictate the reading; the only safe interpretation, and the usual one, is *every session you hold for this principal*. 5. Answer with a `<LogoutResponse>` saying what happened. ## Where the value comes from The index is not the participant's invention. The identity provider chose it when it issued the login statement and the participant stored it alongside its own session record; the logout message quotes it back. That has a practical consequence worth stating plainly: **a participant that discarded the value, or never stored it, cannot key on it.** It is then forced to the crude behaviour — end everything for that principal, or end nothing — and neither is what the authority asked for. The index is also opaque: it is a matching token, not something to parse for a timestamp or a user name. ## The naming half is just as fragile The identifier must be one *both parties currently recognise*. Three ways that goes wrong in practice: - The participant keyed its session on an email address it mapped from an attribute, while the authority sends a persistent pseudonymous identifier. Nothing matches, and the participant answers that it found no session. - The identifier is encrypted as `<saml:EncryptedID>` and the participant cannot decrypt it, so it cannot even determine whose session is meant. - A per-login transient identifier was received at login, and the participant stored the mapped local user rather than the identifier itself. In all three the message is well-formed, signed and useless. This is the unglamorous reason so many logout integrations report success and leave sessions running. ## What good handling looks like - Store the session index with the session record at login, indexed for lookup, not in a log line. - Store the identifier exactly as received, including its format, and match on that pair rather than on a derived local user name. - Accept multiple `<samlp:SessionIndex>` children; an authority ending several sessions at once is entitled to list them in one message. - Treat an absent index as a request to end everything for that principal, and say so in the answer. - Never widen the blast radius silently: if you cannot match the index but can match the principal, ending all of that person's sessions is defensible, ending nothing is defensible, and doing either without recording it is not. The interview version of this is short: the identifier says *who*, the index says *which login*, and a logout that carries only the first is a logout that either does too much or does nothing.

  • What does a `<LogoutRequest>` carrying no `SessionIndex` child mean to the recipient?
    The specification does not fix the reading, and the only safe one is every session the recipient holds for that principal. Recipients that instead end nothing turn a silent omission into a live session; recipients that end everything may sign the user out of a device they are still using. Either way the answer should state what was done, so the authority is not guessing.
  • Why can a persistent identifier not stand in for a `SessionIndex`?
    A persistent identifier is stable for the same person across every login, which is exactly what makes it useless for picking one login out of several. It identifies the human, not the episode. Ending by identifier alone signs the user out of the tablet they are still working on when only the laptop session was supposed to end.
  • May a single `<LogoutRequest>` list more than one `SessionIndex`?
    Yes — the child is zero or more. An authority ending several of a principal's sessions at one participant may list each index in the same message, and the participant ends exactly those. This is why treating the element as a single optional value, rather than a collection, quietly loses sessions in implementations.

saying these in an interview costs you the question

  • Thinks `SessionIndex` identifies the user rather than one session.
  • Assumes a `<LogoutRequest>` may carry only one `SessionIndex` child.
  • Believes the identifier alone is enough to end the right session.
  • Thinks the service provider invents its own `SessionIndex` value.
  • Treats a per-login transient identifier as stable across separate logins.
open as a page

Why does SAML 2.0 define a Single Logout profile instead of leaving each service provider to drop its own session?

level: juniorimportance: should knowfreq 46%

basics

~20 s

One federated login creates one session at the identity provider, acting as session authority, plus one at each service provider. Dropping only the local session leaves the others live, so the Single Logout profile propagates the exit to every participant.

open as a page

What does a SAML `<LogoutResponse>` whose second-level `<StatusCode>` is `PartialLogout` tell the initiator?

level: middleimportance: should knowfreq 41%

basics

~20 s

The exchange itself worked, the outcome did not: the session authority ended what it could, and at least one session participant never confirmed. The top-level code stays Success, so a caller reading only that level reports a clean logout it did not get.

open as a page

In SAML Single Logout, why can the SOAP back channel fail to end a session a front-channel chain would end?

level: seniorimportance: should knowfreq 34%

basics

~20 s

A SOAP logout is a server-to-server call and can only delete state the participant holds server-side. A participant whose session exists solely as a cookie in the user agent has nothing to delete, so only a front-channel hop through the browser can end it.

open as a page