Table of Contents

RP-initiated logout (end session)

A client signs its user out of the authorization server too. It sends the browser to the end-session endpoint with the identity token it was issued as id_token_hint; because the hint identifies the session, the server ends it without asking, tells every client in the session — here by a back-channel logout token posted to the web client's BackChannelLogoutUri — and shows the signed-out page, which loads the clients' front-channel logout URIs and offers the way back to post_logout_redirect_uri with the client's state. The client is the demo host's web. The test answers the back-channel POST itself (it replaces the primary handler of the server's named back-channel HttpClient, so the request the server makes is real and nothing leaves the process).

Follows: OpenID Connect RP-Initiated Logout 1.0, OpenID Connect Back-Channel Logout 1.0, OpenID Connect Front-Channel Logout 1.0.

Recorded from the demo host (src/Fdyay.IdentityServer.Host) by the EndSessionFlow test on every CI run. A value in braces, such as {access_token_1}, is one that differs on every run; the same placeholder is the same value wherever it appears. See how these pages are made.

sequenceDiagram
    participant Browser
    participant AuthorizationServer as Authorization Server
    participant Client
    Note over Browser,AuthorizationServer: The user signed in to the web client through the<br/>authorization code flow, and the client kept the<br/>identity token.
    Browser->>AuthorizationServer: GET /connect/endsession
    AuthorizationServer->>Client: POST https://client.example.com/signout-back
    Client-->>AuthorizationServer: 200 OK
    AuthorizationServer-->>Browser: 302 Found to /account/logout
    Browser->>AuthorizationServer: GET /account/logout
    AuthorizationServer-->>Browser: 302 Found to /Account/LoggedOut
    Browser->>AuthorizationServer: GET /Account/LoggedOut
    AuthorizationServer-->>Browser: 200 OK

The user signed in to the web client through the authorization code flow, and the client kept the identity token.

1. The client sends the browser to the end-session endpoint

The server validates the hint, ends the session — the session cookies are deleted — and notifies the session's clients before it answers. The logout_token it posts is a JWT naming the user and the session, with the back-channel logout event.

Browser → Authorization Server

GET /connect/endsession?id_token_hint={id_token_1}
    &post_logout_redirect_uri=https%3A%2F%2Fclient.example.com%2Fsignout-callback
    &state=x9q2w HTTP/1.1
Cookie: .AspNetCore.Antiforgery.{suffix}={antiforgery_cookie_1}; idsrv={authentication_cookie_1}

{id_token_1} decoded — the header, then the payload; the signature is not shown:

{
  "alg": "RS256",
  "kid": "{key_id_1}",
  "typ": "JWT"
}
{
  "aud": [
    "web"
  ],
  "iss": "http://localhost",
  "iat": {iat},
  "nbf": {iat},
  "exp": {iat + 300},
  "jti": "{jti_1}",
  "azp": "web",
  "sub": "alice",
  "auth_time": {auth_time},
  "idp": "local",
  "amr": [
    "pwd"
  ],
  "name": "Alice Smith",
  "nonce": "n-0S6_WzA2Mj",
  "sid": "{session_id_1}",
  "at_hash": "{at_hash_1}"
}

Authorization Server → Client

POST https://client.example.com/signout-back HTTP/1.1
Content-Type: application/x-www-form-urlencoded

logout_token={logout_token_1}

{logout_token_1} decoded — the header, then the payload; the signature is not shown:

{
  "alg": "RS256",
  "kid": "{key_id_1}",
  "typ": "logout+jwt"
}
{
  "aud": [
    "web"
  ],
  "iss": "http://localhost",
  "iat": {iat},
  "nbf": {iat},
  "exp": {iat + 300},
  "jti": "{jti_2}",
  "events": {
    "http://schemas.openid.net/event/backchannel-logout": {}
  },
  "sub": "alice",
  "sid": "{session_id_1}",
  "logout_reason": "user_logout"
}

Client → Authorization Server

HTTP/1.1 200 OK

Authorization Server → Browser

HTTP/1.1 302 Found
Location: /account/logout?logoutId={logout_id_1}
Cache-Control: no-cache,no-store
Pragma: no-cache
Set-Cookie: idsrv=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; samesite=none; httponly
Set-Cookie: idsrv.session=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/

2. The logout page has nothing to ask

The session is already gone, so it moves straight on to the signed-out page.

Browser → Authorization Server

GET /account/logout?logoutId={logout_id_1} HTTP/1.1
Cookie: .AspNetCore.Antiforgery.{suffix}={antiforgery_cookie_1}

Authorization Server → Browser

HTTP/1.1 302 Found
Location: /Account/LoggedOut?logoutId={logout_id_1}

3. The signed-out page

It loads the web client's front-channel logout URI in an iframe and links back to https://client.example.com/signout-callback?state=x9q2w.

Browser → Authorization Server

GET /Account/LoggedOut?logoutId={logout_id_1} HTTP/1.1
Cookie: .AspNetCore.Antiforgery.{suffix}={antiforgery_cookie_1}

Authorization Server → Browser

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8

(an HTML page, not shown)