# OP session-management iframe (OIDC Session Management 1.0 §3.2)

`GET /check_session_iframe`

Serves the OP iframe an RP embeds to poll the end user's login status at
the provider without network traffic. It accepts `postMessage` payloads of
the form `<client_id> <session_state>` from the RP iframe and posts back
`unchanged`, `changed`, or `error`.

**Relying parties must append `?client_id=<their client id>`.** §3.2
requires the OP iframe to "enforce that the caller is from an expected
origin" and to "reject postMessage requests from any other source origin",
and the only way this page can know which origins are expected is to be
told which client is asking: the parameter selects that client's registered
redirect-URI origins as the page's allowlist.

A page loaded from the bare URL — which is what the `check_session_iframe`
metadata value is, since one fixed URL cannot name a client — therefore has
an empty allowlist and answers nobody. Not knowing which origins are
expected is not a license to accept every origin.

A rejected caller receives no reply at all. §3.1 assigns a meaning to each
of the three replies and none of them means "you should not be talking to
me", and answering would confirm to an unexpected origin that it has found
the OP iframe.

## 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

- `client_id` (string, optional): The RP whose registered redirect-URI origins become the page's postMessage allowlist, and the only client the page will answer about. Nominally optional, because the metadata value cannot carry one; a page loaded without it answers no origin at all. An unknown client_id is treated the same as none, so this cannot be used to enumerate clients. Constraints: at most 256 characters.

## 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 OP session-management iframe document.

Content type: `text/html`

Headers:
- `Content-Security-Policy` (string): Per-response policy carrying the script nonce and a framable frame-ancestors.
- `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 'https://auth.example.com/check_session_iframe'
```
