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

OID4VCI credential-request key proof of type `jwt`
(draft-ietf-oauth-openid4vci §8.2.1.1).

When a wallet asks the credential endpoint for a credential, it proves
possession of the key the issued credential will be bound to (its `cnf`) with
a `proof` object `{"proof_type": "jwt", "jwt": <JWS>}`. The proof JWS is typed
`openid4vci-proof+jwt`; its header carries the holder's public key (as `jwk`);
its payload carries:

  * `aud` - the Credential Issuer Identifier (this issuer). REQUIRED.
  * `iat` - issuance time. REQUIRED, and must be fresh.
  * `nonce` - the `c_nonce` the issuer previously handed out, when it did.
  * `iss` - the client_id, for the authorized code flow.

`verify_jwt/2` validates the proof and returns the holder public JWK (and its
RFC 7638 thumbprint) so the caller binds the credential to it via `cnf`.

This is the issuance-time sibling of `Attesto.DPoP` / `Attesto.SdJwt`'s Key
Binding JWT: same "prove you hold this key" shape, different claim set.
Conn-free and fail-closed.

## Optional key attestation cross-check

A Wallet MAY additionally carry a key attestation (`Attesto.KeyAttestation`)
in the proof's `key_attestation` JOSE header, vouching that the proof's
`jwk` is held in attested secure storage (OID4VCI Appendix D). This is
off by default - passing neither `:key_attestation_trusted_jwks` nor
`:require_key_attestation` reproduces the exact behavior of every prior
release. Supplying `:key_attestation_trusted_jwks` opts a caller into
verifying a present `key_attestation` header and rejecting a proof whose
key is not among its `attested_keys`; `:require_key_attestation` additionally
rejects a proof that carries no `key_attestation` header at all.

# `verified`

```elixir
@type verified() :: %{
  jwk: map(),
  jkt: String.t(),
  key_attestation: Attesto.KeyAttestation.verified() | nil
}
```

# `verify_error`

```elixir
@type verify_error() ::
  :invalid_proof
  | :invalid_typ
  | :invalid_alg
  | :missing_jwk
  | :invalid_jwk
  | :invalid_signature
  | :invalid_audience
  | :invalid_nonce
  | :invalid_iat
  | :invalid_issuer
  | :missing_key_attestation
  | :invalid_key_attestation
  | :key_not_attested
```

# `verify_jwt`

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

Verify a `jwt` credential-request key proof.

Required opts:

  * `:issuer` - the Credential Issuer Identifier the proof's `aud` must equal.

Optional opts:

  * `:nonce` - the expected `c_nonce`. When set, the proof MUST carry a
    matching `nonce`; when omitted, no `nonce` is required (issuers that do
    not use `c_nonce`).
  * `:client_id` - when set, the proof's `iss` MUST equal it.
  * `:now` - clock reference (DateTime or unix seconds).
  * `:max_age_seconds` - how far in the past `iat` may be. Default
    300.
  * `:accepted_algs` - JWS algorithms accepted. Defaults to
    `Attesto.SigningAlg.fapi_algs/0`.
  * `:key_attestation_trusted_jwks` - opts into verifying a `key_attestation`
    JOSE header (see "Optional key attestation cross-check" above) against
    these trusted keys and rejecting a proof whose key is not among the
    attestation's `attested_keys`. Omitted (the default), no such header is
    looked at.
  * `:require_key_attestation` - when true, a proof with no `key_attestation`
    header is rejected. Only meaningful alongside
    `:key_attestation_trusted_jwks`; defaults to `false`.

Returns `{:ok, %{jwk: holder_public_jwk, jkt: thumbprint, key_attestation:
verified_attestation_or_nil}}`. `key_attestation` is `nil` unless
`:key_attestation_trusted_jwks` was supplied and a `key_attestation` header
was present and verified.

---

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