# `Attesto.Siop`
[🔗](https://github.com/XukuLLC/attesto/blob/v2.1.0/lib/attesto/siop.ex#L1)

SIOPv2 Self-Issued ID Token verification for the Relying Party role.

A Self-Issued ID Token is not verified against an issuer-owned JWKS. The
holder either embeds a public key in `sub_jwk` and uses its RFC 7638 SHA-256
thumbprint as `sub`, or uses a self-contained `did:jwk` / `did:key` subject
whose resolved verification method signs the token. `verify/2` ties the
subject, verification key, and signature together before returning the
verified subject and public key.

This module implements the JWK Thumbprint Subject Syntax Type from
Self-Issued OpenID Provider v2 draft 13, section 11.1. It accepts `sub_jwk`
in the payload, as the current draft specifies, and in the protected JOSE
header for wallet interoperability. If both locations are present, their
JWK maps must be identical.

The current draft identifies a Self-Issued ID Token with `iss == sub`.
`https://self-issued.me/v2`, used by the earlier static-discovery model, is
also accepted for JWK-thumbprint subjects. DID subjects require `iss == sub`
and a protected `kid` naming the method-defined verification method. Only
connection-free `did:jwk` and `did:key` are resolved here; network-backed DID
methods remain host-owned and fail closed.

Verification is conn-free and fail-closed:

  * the compact JWS must be canonical and carry no unsupported critical
    headers;
  * `alg` must be an Attesto-supported asymmetric algorithm allowed by RP
    policy and compatible with a public verification JWK;
  * the signature must verify strictly with the embedded or DID-resolved
    holder key;
  * `sub` must exactly equal the key's RFC 7638 thumbprint, or be the
    `did:jwk` / `did:key` from which the key and protected `kid` are derived;
  * `iss` must equal `sub` for DID subjects, or either `sub` or
    `https://self-issued.me/v2` for JWK-thumbprint subjects;
  * `aud` must contain the RP Client ID and `nonce` must exactly match the
    Authentication Request;
  * `exp` and `iat` are required non-negative NumericDates; `nbf` is optional
    but, when present, must also be a non-negative NumericDate. Expired and
    not-yet-valid tokens are rejected.

Claims other than the cryptographically bound `sub` remain self-attested.

# `verified`

```elixir
@type verified() :: %{subject: String.t(), jwk: map()}
```

# `verify_error`

```elixir
@type verify_error() ::
  :invalid_token
  | :unsupported_critical_header
  | :unexpected_typ
  | :invalid_alg
  | :missing_sub_jwk
  | :invalid_sub_jwk
  | :invalid_signature
  | :invalid_subject
  | :invalid_issuer
  | :invalid_audience
  | :invalid_nonce
  | :invalid_claims
  | :expired
  | :not_yet_valid
```

# `verify_opts`

```elixir
@type verify_opts() :: [
  audience: String.t(),
  nonce: String.t(),
  now: DateTime.t() | non_neg_integer(),
  accepted_algs: [Attesto.SigningAlg.alg()]
]
```

# `verify`

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

Verify a Self-Issued ID Token and return its subject and holder public JWK.

JWK-thumbprint subjects may carry `sub_jwk` in the payload or protected
header. A `did:jwk` subject must instead carry protected `kid` equal to
`did:jwk:...#0`; a `did:key` subject must carry protected `kid` equal to the
DID followed by `#` and its multibase value. A DID response containing
`sub_jwk`, omitting `kid`, or naming any other verification method is
rejected.

Required options:

  * `:audience` - the RP Client ID sent in the Authentication Request. The
    token's `aud` may be this string or an all-string array containing it.
  * `:nonce` - the nonce sent in the Authentication Request. SIOPv2 requires
    it to be present and identical in the Self-Issued ID Token.

Optional options:

  * `:now` - clock reference as a `DateTime` or Unix seconds.
  * `:accepted_algs` - holder signature algorithms accepted by RP policy.
    Defaults to `Attesto.SigningAlg.allowed/0`; `none`, MAC algorithms, and
    algorithms unsupported by Attesto remain rejected even if listed.

The convenience `verify/3` form accepts `id_token`, `audience`, and `nonce`
as positional arguments and applies the default clock and algorithm policy.

# `verify`

```elixir
@spec verify(String.t(), String.t(), String.t()) ::
  {:ok, verified()} | {:error, verify_error()}
```

Verify a Self-Issued ID Token against an RP Client ID and request nonce.

---

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