Attesto.CredentialProof (Attesto v1.11.0)

Copy Markdown View Source

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.

Summary

Functions

Verify a jwt credential-request key proof.

Types

verified()

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

verify_error()

@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

Functions

verify_jwt(proof, opts)

@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.