# `Attesto.RequestObject`
[🔗](https://github.com/XukuLLC/attesto/blob/v1.2.2/lib/attesto/request_object.ex#L1)

Signed OpenID Connect Request Object verification (JAR, RFC 9101 / OIDC §6.1).

This module verifies a compact JWT request object against trusted client
JWKs supplied by the host. It deliberately rejects unsigned request objects:
a host that wants request objects is opting into integrity protection, not a
second unsigned parameter encoding.

# `verify_error`

```elixir
@type verify_error() ::
  :invalid_request_object
  | :request_not_supported
  | :invalid_signature
  | :invalid_issuer
  | :invalid_audience
  | :invalid_typ
  | :expired
  | :not_yet_valid
  | :unsupported_critical_header
```

# `verify_opts`

```elixir
@type verify_opts() :: [
  now: DateTime.t() | non_neg_integer(),
  issuer: String.t() | nil,
  audience: String.t() | [String.t()],
  accepted_algs: [Attesto.SigningAlg.alg()],
  require_nbf: boolean(),
  max_nbf_age_seconds: pos_integer() | nil,
  require_exp: boolean(),
  require_iat: boolean(),
  require_jti: boolean(),
  require_client_id_claim: boolean(),
  max_lifetime_seconds: pos_integer() | nil,
  accepted_typ: [String.t() | nil] | nil,
  string_valued_claims: [String.t()]
]
```

# `verify`

```elixir
@spec verify(String.t(), map() | [map()] | map(), verify_opts()) ::
  {:ok, map()} | {:error, verify_error()}
```

Verify and return a string-keyed parameter map from a signed request object.

The object must carry `iss`, `client_id`, and `aud`. `iss` must match the
object's `client_id` and the caller-supplied `:issuer`; `aud` must match the
caller-supplied `:audience`.

Opts implementing the RFC 9101 / FAPI Message Signing 2.0 §5.3.1 strict-JAR
policy. Every one defaults to the lenient JAR/OIDC §6.1 behaviour, so a
caller that passes none observes no change:

  * `:accepted_algs` - JOSE algorithms a candidate trusted key may use.
    Defaults to `SigningAlg.fapi_algs/0` (PS256, ES256, EdDSA).
  * `:require_nbf` - when `true`, reject an object without an `nbf` claim.
    Defaults to `false`. (RFC 9101 / FAPI Message Signing 2.0 §5.3.1.)
  * `:max_nbf_age_seconds` - when set, reject an `nbf` older than `now - N`.
    Defaults to `nil` (no lower bound).
  * `:require_exp` - when `true`, reject an object without an `exp` claim.
    Defaults to `false`.
  * `:require_iat` - when `true`, reject an object without a valid `iat`
    NumericDate claim. Defaults to `false`. (CIBA Core §7.1.1 makes `iat`
    REQUIRED in a signed authentication request.)
  * `:require_jti` - when `true`, reject an object without a non-empty
    string `jti` claim. Defaults to `false`. (CIBA Core §7.1.1 makes `jti`
    REQUIRED; replay tracking against it is the caller's concern.)
  * `:require_client_id_claim` - when `false`, skip the requirement that the
    object carry a `client_id` claim equal to `iss`. RFC 9101 request
    objects carry `client_id`; a CIBA signed authentication request
    (CIBA Core §7.1.1) does not - its `iss` alone names the client and is
    still matched against the caller-supplied `:issuer`. Defaults to `true`
    (the RFC 9101 behaviour).
  * `:max_lifetime_seconds` - when set, require valid `nbf` and `exp`
    NumericDate anchors and reject an `exp` greater than `nbf + N`. Defaults
    to `nil` (no lifetime bound).
  * `:accepted_typ` - when a list, require the JOSE header `typ` to be a
    member; `nil` in the list permits an absent `typ`. Defaults to `nil`,
    which accepts any `typ` including its absence.
  * `:string_valued_claims` - when a list of claim names, each named claim
    that is present MUST be a JSON string, else `:invalid_request_object`.
    This runs BEFORE the claim → parameter coercion, so a non-string value
    (number, boolean, array, object) is rejected rather than stringified /
    space-joined / JSON-encoded, and a malformed value can never reach
    `claims_to_params/1`'s coercion (so it cannot raise). Defaults to `[]`
    (no typing constraint - the lenient JAR/OIDC §6.1 coercion). CIBA Core
    §7.1.1 uses this to require its known authentication-request parameters
    be JSON strings in a signed request; RFC 9101 JAR leaves it unset.

# `verify_with_claims`

```elixir
@spec verify_with_claims(String.t(), map() | [map()] | map(), verify_opts()) ::
  {:ok, map(), map()} | {:error, verify_error()}
```

Like `verify/3`, but also returns the raw verified JWT claims alongside the
parameter map: `{:ok, params, claims}`.

A profile that must act on reserved claims the parameter map deliberately
drops - CIBA Core §7.1.1 needs the signed request's `jti` and `exp` to
implement replay defense at the host boundary - uses this to read them
without decoding/verifying the JWT a second time. `params` is identical to
what `verify/3` returns.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
