OID4VCI Wallet (Holder) issuance flow (draft-ietf-oauth-openid4vci).
request_credential/3 drives the pre-authorized_code issuance flow end to
end:
- exchange the offer's pre-authorized code for an access token
(
AttestoClient.Token.exchange_pre_authorized_code/2) - skipped when an:access_tokenis already supplied, e.g. obtained separately through the authorization_code flow (AttestoClient.AuthorizationCode); - fetch a fresh
c_noncewhen the issuer advertises a nonce endpoint; - build the holder key proof (
AttestoClient.Wallet.Proof); - POST the Credential Request and parse the Credential Response
(
Attesto.CredentialResponseshape) - atransaction_idresponse is returned as a:pendingmarker (deferred issuance is not polled here); and - verify each returned credential with the attesto verifier matching
:format(Attesto.SdJwtVc,Attesto.JwtVc, orAttesto.Mdoc).
All HTTP goes through AttestoClient.OAuthHTTP, so the flow is mockable
the same way as the rest of this library (req_options: [plug: ...]).
Summary
Functions
Run the wallet-holder issuance flow for offer and return the verified,
held credential(s) (or a pending marker for deferred issuance).
Types
@type opt() :: {:credential_configuration_id, String.t()} | {:credential_endpoint, String.t()} | {:token_endpoint, String.t()} | {:nonce_endpoint, String.t()} | {:notification_endpoint, String.t()} | {:notification_event, String.t()} | {:access_token, String.t()} | {:tx_code, String.t()} | {:client_id, String.t()} | {:client_auth, term()} | {:format, String.t()} | {:trusted, term()} | {:verify_opts, keyword()} | {:proof_alg, String.t()} | {:proof_kid, String.t()} | {:key_attestation, String.t() | ([map()], String.t() | nil -> {:ok, String.t()} | {:error, term()})} | {:dpop, AttestoClient.Wallet.Proof.jwk()} | {:now, integer()} | {:req_options, keyword()} | {:timeout, pos_integer()}
@type result() :: %{ credentials: [held_credential() | pending_credential()], c_nonce: String.t() | nil }
Functions
@spec request_credential( AttestoClient.Wallet.CredentialOffer.t(), AttestoClient.Wallet.Proof.jwk() | [AttestoClient.Wallet.Proof.jwk()], [opt()] ) :: {:ok, result()} | {:error, term()}
Run the wallet-holder issuance flow for offer and return the verified,
held credential(s) (or a pending marker for deferred issuance).
Required options: :credential_endpoint, :format (one of "vc+sd-jwt",
"dc+sd-jwt", "jwt_vc_json", or "mso_mdoc"), and :trusted (the
Credential Issuer's verification key material, in the shape the matching
attesto verifier expects - a JWKS/JWK list for Attesto.SdJwtVc and
Attesto.JwtVc, a single JWK/PEM for Attesto.Mdoc). :verify_opts are
forwarded to that verifier (e.g. :accepted_algs, :now).
Unless :access_token is supplied, the flow performs the
pre-authorized_code token exchange itself and requires :token_endpoint
and, when the offer's grant carries one, :tx_code. :client_id and
:client_auth authenticate that token request exactly as in
AttestoClient.Token, and, when present, :client_id is also carried as
the proof's iss.
:nonce_endpoint, when supplied, is used to fetch a fresh c_nonce before
building the proof. :credential_configuration_id selects which of the
offer's configuration ids to request; it defaults to the offer's only id
when there is exactly one.
:dpop (a JOSE.JWK or JWK map) sender-constrains the flow with RFC 9449
DPoP proofs. The one key is used at the token endpoint (binding the access
token to its jkt) and at the Credential Endpoint (where the proof carries
the ath binding to that token), and a use_dpop_nonce challenge is retried
once - see AttestoClient.DPoP.
:key_attestation (a compact JWT from AttestoClient.KeyAttestation.build/2)
is carried in the holder proof's key_attestation header, vouching to the
issuer that the holder key is held in secure storage (a HAIP requirement).
:notification_endpoint, when supplied, makes the wallet POST an OID4VCI §10
Notification acknowledging the credential once issuance succeeds and the
response carried a notification_id; :notification_event overrides the
default "credential_accepted". The returned result always includes the
issuer's notification_id (or nil).