OpenID Connect Session Management 1.0 — the session_state value.
Session Management lets a Relying Party poll, from JavaScript, whether the
End-User's login state at the OP has changed without a network round trip:
the RP posts client_id + " " + session_state to the OP's
check_session_iframe, which recomputes the value from the current OP
browser state and answers unchanged or changed (§3.1/§3.2).
session_state is returned to the client as an additional authorization
response parameter (§2) and is computed as (§3.2):
hex(SHA256(client_id <> " " <> origin <> " " <> op_browser_state <> " " <> salt)) <> "." <> saltclient_id— the RP's client identifier.origin— the origin of the Authentication Response'sredirect_uri(seeorigin/1), which is what the browser reports asMessageEvent.originwhen the RP's iframe posts to the OP iframe.op_browser_state— the OP User Agent state: an opaque value stored in a JavaScript-readable cookie at the OP origin that changes on login/logout, so a recomputation with a stale value yieldschanged.salt— a random per-response salt carried in cleartext after the.so the OP iframe can recompute the hash.
The hash is lowercase hex, matching the §3.2 example's CryptoJS.SHA256
string form, so the OP iframe's JavaScript recomputation compares equal. The
value contains no space character (§2).
Like the rest of attesto core this module is pure: the host owns the browser
state cookie and the check_session_iframe page; this module owns the
computation both sides must agree on.
OP-owned, login-bound browser state
The session_state recipe treats op_browser_state as an opaque string, so
the OP is free to give that string internal structure. Session Management
requires the OP browser state to be OP-owned (an RP-visible / injected
value must not be able to forge unchanged) and to change when the
End-User's login state changes (§3.2). mint_browser_state/2 and
browser_state_valid?/3 give the value both properties without changing what
the iframe hashes:
random . login_tag . macrandom— fresh 128-bit entropy, unguessable cross-origin;login_tag—HMAC(secret, login_binding), a stable fingerprint of the current End-User login state (subject / auth_time / sid); a re-auth or account switch changes it, so a stale cookie failsbrowser_state_valid?/3and the OP rotates;mac—HMAC(secret, random . login_tag), so a value the OP did not mint (a cookie injected by a sibling/parent-domain origin) cannot verify.
Only the OP knows secret. The whole random . login_tag . mac string is
still what compute/4 hashes as op_browser_state, so the iframe recipe is
unchanged.
Summary
Functions
Whether value is an OP browser-state string this OP minted (under secret)
for the current login_binding.
Compute the session_state for an authorization response (§3.2).
A fresh OP browser state value.
A fresh random salt for compute/4 (unpadded URL-safe Base64, no spaces or dots).
Mint an OP browser-state value that is OP-owned and bound to the End-User's current login state (see the module doc).
The browser-form origin of uri (RFC 6454): scheme://host, with the port
appended only when it is not the scheme's default — exactly the string the
browser reports as MessageEvent.origin for a page loaded from uri.
Functions
Whether value is an OP browser-state string this OP minted (under secret)
for the current login_binding.
Returns false — so the caller mints a fresh value — when either:
- the value is malformed or its MAC does not verify: a value the OP never minted (a forged / cross-origin-injected cookie), which must not be trusted as authoritative browser state; or
- the MAC verifies but the embedded
login_tagis for a different login state: the End-User re-authenticated or switched accounts, so the OP browser state MUST rotate (Session Management 1.0 §3.2) and any earlier RPsession_statebecomeschanged.
Both comparisons are constant-time.
Compute the session_state for an authorization response (§3.2).
origin must be a browser-form origin (scheme://host[:port], default port
omitted) — derive it from the response's redirect_uri with origin/1.
salt defaults to a fresh generate_salt/0.
@spec generate_browser_state() :: String.t()
A fresh OP browser state value.
The host stores it in a JavaScript-readable cookie at the OP origin (§3.2 —
the check_session_iframe script must read it, so HttpOnly cannot be set)
and changes it when the End-User's login state changes (login/logout).
@spec generate_salt() :: String.t()
A fresh random salt for compute/4 (unpadded URL-safe Base64, no spaces or dots).
Mint an OP browser-state value that is OP-owned and bound to the End-User's current login state (see the module doc).
secret is an OP-only HMAC key; login_binding is a caller-chosen string
that captures the current login state (e.g. subject, auth_time, and sid
joined together). The returned random . login_tag . mac string contains no
space or .-ambiguity in its parts (each part is base64url-no-pad), so it is
a valid op_browser_state for compute/4 and splits back cleanly in
browser_state_valid?/3.
The browser-form origin of uri (RFC 6454): scheme://host, with the port
appended only when it is not the scheme's default — exactly the string the
browser reports as MessageEvent.origin for a page loaded from uri.
Returns {:ok, origin} or {:error, :invalid_uri} for a URI with no
scheme/host (a session_state computed over a malformed origin could never
compare equal in the browser, so fail closed instead).