Attesto.EndSession (Attesto v0.13.0)

Copy Markdown View Source

Validate an OpenID Connect RP-Initiated Logout request (OpenID Connect RP-Initiated Logout 1.0 §2-3).

The end-session endpoint is where a Relying Party sends the End-User's browser to log out at the OP. Like the rest of attesto core this module is pure — it parses and validates the request parameters and never touches a store, a session, or a Plug.Conn. The host's controller owns loading the client, terminating the browser session, and (for Back-Channel Logout) fanning logout tokens out to the RPs.

Two steps, because the second needs data the controller must fetch in between:

  1. parse/2 validates the request parameters, verifies the id_token_hint (tolerating expiry — see Attesto.IDToken.verify_logout_hint/2), and resolves the Relying Party client_id and the session identifiers (sub / sid) the back-channel fan-out will key on. It returns a t/0 the controller uses to load the client's registered post_logout_redirect_uris.
  2. confirm_redirect/2 checks the requested post_logout_redirect_uri against that registered list (exact string match, RP-Initiated Logout §2/§3 — no normalization, no prefix matching) and returns the final redirect target with state appended, or :no_redirect when the RP asked for none.

Resolving the client before validating the redirect URI is what makes an open-redirect impossible: a post_logout_redirect_uri is only ever honored when it exactly matches a value the client registered.

Summary

Types

t()

A parsed, hint-verified end-session request.

Functions

Confirm the request's post_logout_redirect_uri against the Relying Party's registered list and compute the final redirect target.

Parse and validate the end-session request params (string- or atom-keyed).

Types

parse_error()

@type parse_error() :: :invalid_id_token_hint | :client_id_mismatch

t()

@type t() :: %Attesto.EndSession{
  client_id: String.t() | nil,
  logout_hint: String.t() | nil,
  post_logout_redirect_uri: String.t() | nil,
  sid: String.t() | nil,
  state: String.t() | nil,
  subject: String.t() | nil,
  ui_locales: String.t() | nil
}

A parsed, hint-verified end-session request.

  • :client_id - the Relying Party, resolved from the id_token_hint's aud and/or the client_id parameter (nil when the request carried neither, in which case no post_logout_redirect_uri can be honored).
  • :subject / :sid - the session identifiers from the id_token_hint (nil when no hint was supplied); the keys a Back-Channel Logout fan-out uses to find the RP sessions to notify.
  • :post_logout_redirect_uri / :state - the requested return target and opaque state, echoed back only after the URI is confirmed registered.
  • :logout_hint / :ui_locales - passthrough hints (RP-Initiated Logout §2) for the host's logout-confirmation UI.

Functions

confirm_redirect(request, registered)

@spec confirm_redirect(t(), [String.t()]) ::
  {:ok, String.t() | :no_redirect} | {:error, :invalid_post_logout_redirect_uri}

Confirm the request's post_logout_redirect_uri against the Relying Party's registered list and compute the final redirect target.

registered_uris is the client's registered post_logout_redirect_uris (the host loads them from its Attesto.ClientStore between parse/2 and this call). Matching is exact-string (RP-Initiated Logout §2/§3).

Returns:

  • {:ok, :no_redirect} - the request asked for no return URI; the host should render its own logged-out page.
  • {:ok, url} - the validated post_logout_redirect_uri with the request's state appended as a query parameter when present.
  • {:error, :invalid_post_logout_redirect_uri} - the requested URI is not registered (or the RP could not be identified), so it MUST NOT be used.

parse(config, params)

@spec parse(Attesto.Config.t(), map()) :: {:ok, t()} | {:error, parse_error()}

Parse and validate the end-session request params (string- or atom-keyed).

Recognized parameters (OpenID Connect RP-Initiated Logout 1.0 §2): id_token_hint, client_id, post_logout_redirect_uri, state, logout_hint, ui_locales.

When id_token_hint is present it is verified via Attesto.IDToken.verify_logout_hint/2 (signature + issuer, expiry tolerated); a hint that fails verification is :invalid_id_token_hint. The Relying Party client_id is taken from the hint's aud; if the client_id parameter is also present it MUST equal it (:client_id_mismatch, RP-Initiated Logout §2). With no hint, the client_id parameter alone identifies the RP.

Returns {:ok, %Attesto.EndSession{}} or {:error, reason}.