# RP-Initiated Logout (OpenID Connect)

`GET /end_session`

Logs the end user out at the OP. If a valid, exactly-registered
`post_logout_redirect_uri` is supplied, the OP redirects the user agent
there with `state` appended; otherwise a logged-out page is shown. The
OP never redirects to an unregistered URI.

## Authorization

None: this endpoint does not take API credentials.

This endpoint reads the browser session from the `Cookie` header: The request Cookie header; the interactive login / pending-auth session cookie, and the browser marker a sign-in is recognized by, are read from it.

This endpoint is rate limited; it answers `429` when a limit is hit.

## Query parameters

- `id_token_hint` (string, optional): A previously issued ID token identifying the session to end.
- `logout_hint` (string, optional)
- `client_id` (string, optional)
- `post_logout_redirect_uri` (string (uri), optional): Must exactly match a pre-registered post-logout redirect URI.
- `state` (string, optional)
- `ui_locales` (string, optional)

## Headers

- `Cookie` (string, optional): The request Cookie header; the interactive login / pending-auth session cookie, and the browser marker a sign-in is recognized by, are read from it.

## Responses

### 200 OK

The front-channel logout page: one `<iframe>` per participating relying
party, plus a redirect to the post-logout target. Served instead of the
302 only when a participating client registered a
`frontchannel_logout_uri`.

Delivery through this page is BEST EFFORT and unverifiable — the frames
are cross-origin, so the OP can read neither their status nor their
body, and §4.1 warns that a browser blocking third-party content will
keep the RP from seeing its own cookies at all. Back-Channel Logout 1.0
is the mechanism that actually confirms anything.

Content type: `text/html`

Headers:
- `Set-Cookie` (array of strings)
- `Cache-Control` = `no-store`
- `Pragma` = `no-cache`
- `Content-Security-Policy` = `default-src 'none'; style-src 'unsafe-inline'; frame-src https: http:; base-uri 'none'; form-action 'none'; frame-ancestors 'none'`: Locks the logout page down to inline styles and cross-origin frames.

### 302 Found

Redirect to the validated post_logout_redirect_uri (or the hosted UI), with the session cookie cleared.

Headers:
- `Location` (string)
- `Set-Cookie` (array of strings)
- `Cache-Control` = `no-store`
- `Pragma` = `no-cache`

### 429 Too Many Requests

Too many requests.

Content type: `application/problem+json`

RFC 9457 (formerly RFC 7807) problem details (application/problem+json). `code` is a stable machine-readable slug for the problem kind (it mirrors the last path segment of `type`), so consumers can branch on it without parsing prose. `type` is `https://nauthera.io/problems/<code>`, which resolves to the code's runbook: why it happened, who is affected and what to do (RFC 9457 §3.1.1). A problem without a code has `about:blank`. `errors` is present on validation failures and lists every offending field.

- `title` (string, required): Example: `"Request validation failed"`.
- `status` (integer, required): Example: `400`.
- `type` (string (uri), optional): Example: `"https://nauthera.io/problems/validation_error"`.
- `detail` (string, optional): Example: `"One or more fields did not satisfy the API schema."`.
- `code` (enum, optional): The kind of problem, as a stable machine-readable slug a client branches on and translates rather than parsing `title` or `detail`, which are English prose for logs. A problem whose status says everything the server knows carries no `code`. One of: `"invalid_request"`, `"invalid_credentials"`, `"forbidden"`, `"unauthorized"`, `"insufficient_user_authentication"`, `"protected_account"`, `"password_held_by_directory"`, `"last_role_administrator"`, `"invalid_code"`, `"too_many_requests"`, `"too_many_attempts"`, `"account_locked"`, `"account_unavailable"`, `"already_enrolled"`, `"registration_disabled"`, `"registration_conflict"`, `"captcha_failed"`, `"invalid_reset_link"`, `"password_reused"`, `"password_too_short"`, `"password_too_long"`, `"password_compromised"`, `"invalid_verification_link"`, `"invalid_invitation_link"`, `"mail_relay_missing"`, `"email_unverified"`, `"not_found"`, `"conflict"`, `"unavailable"`, `"busy"`, `"window_too_large"`, `"too_many_series"`, `"internal_error"`, `"rate_limited"`, `"malformed"`, `"body_too_large"`, `"required"`, `"validation_error"`, `"unknown_org"`, `"invalid_token"`, `"invalid_dpop_proof"`, `"use_dpop_nonce"`, `"server_error"`.

  `validation_error` is the one code whose message is not a sentence about
  the code itself: the request failed the schema, and the specifics are
  the `errors` list — one entry per offending field — which a form renders
  on those fields. `required` and `malformed` are spelled like two of the
  per-field violation codes (`FieldError.code`) but mean something else;
  the two vocabularies are told apart by position, never by spelling.

  **Compatibility:** this is a CLOSED enum, with the same cost as
  `AuditAction`: a client generated against an older version of this
  document rejects a problem carrying a code added after it was built.
  Regenerate on upgrade, or decode the field as a plain string. It is
  enumerated anyway so that what a client must translate is knowable
  from the contract alone.
- `instance` (string, optional): Example: `"/introspect"`.
- `request_id` (string, optional): Example: `"8d94ed49-1caf-4ab3-9594-557142f2d023"`.
- `trace_id` (string, optional): Distributed-trace id (hex) for correlating this response with traces and logs. Present only when tracing is enabled and the request was recorded. Opaque and safe to log/share. Example: `"4bf92f3577b34da6a3ce929d0e0e4736"`.
- `errors` (array of objects, optional): Field-level violations (present for validation errors).

  Each item: A single field-level validation violation.
  - `field` (string, required): Offending field, dotted for nested bodies (e.g. child.id). Example: `"token_type_hint"`.
  - `code` (string, required): Machine-readable violation code: required, too_short, too_long, pattern, invalid_format, not_allowed, minimum, maximum, multiple_of, too_few, too_many, not_unique, malformed. Example: `"not_allowed"`.
  - `message` (string, required): Example: `"field token_type_hint must be one of [access_token, refresh_token]"`.

### Error

OAuth2 error response (RFC 6749 §5.2).

Content type: `application/json`

Headers:
- `Cache-Control` = `no-store`
- `WWW-Authenticate` (string)
- `DPoP-Nonce` (string)

An OAuth2 error response (RFC 6749 §5.2). `iss` (RFC 9207) is present on authorization-endpoint error responses so clients can detect mix-up.

- `error` (string, required): A single ASCII error code. Example: `"invalid_request"`.
- `error_description` (string, optional): Example: `"client_id, redirect_uri and response_type are required"`.
- `error_uri` (string (uri), optional): The error code's runbook (RFC 6749 §5.2): `https://nauthera.io/runbooks/oauth/<error>`. Example: `"https://nauthera.io/runbooks/oauth/invalid_request"`.
- `state` (string, optional): Echoed authorization-request state (RFC 6749 §4.1.2.1).
- `iss` (string (uri), optional): Issuer identifier on authorization error responses (RFC 9207).

## Example request

```bash
curl -i 'https://auth.example.com/end_session'
```
