Attesto.CredentialOffer (Attesto v1.10.0)

Copy Markdown View Source

OID4VCI Credential Offer (draft-ietf-oauth-openid4vci §4.1).

Build the string-keyed Credential Offer object and its by-value or by-reference openid-credential-offer:// deep-link forms. This module is pure and conn-free; fetching a referenced offer is the wallet's concern.

Summary

Functions

Build an OID4VCI Credential Offer object.

Build a by-value openid-credential-offer:// deep link.

Build a by-reference openid-credential-offer:// deep link.

Store offer for by-reference retrieval and return the freshly generated, unguessable id to embed in its credential_offer_uri.

JSON-encode an offer for the credential_offer query parameter.

Functions

build(opts)

@spec build(keyword()) :: %{required(String.t()) => term()}

Build an OID4VCI Credential Offer object.

The required :credential_issuer and :credential_configuration_ids options are validated and normalized into a JSON-ready map. When supplied, :grants is normalized to the two OID4VCI grant types supported here: authorization_code and pre-authorized_code.

deep_link(offer, opts \\ [])

@spec deep_link(
  map(),
  keyword()
) :: String.t()

Build a by-value openid-credential-offer:// deep link.

store_by_reference(store, offer, opts \\ [])

@spec store_by_reference(module(), map(), keyword()) :: {:ok, String.t()}

Store offer for by-reference retrieval and return the freshly generated, unguessable id to embed in its credential_offer_uri.

The id is the ONLY thing protecting a by-reference offer: the offer endpoint is unauthenticated by design (OID4VCI §4.1.3, the wallet dereferences it before it has any token), and a pre-authorized offer embeds a redeemable pre-authorized_code. A guessable id therefore lets an attacker enumerate the offer endpoint, read a victim's offer, and redeem its code first. This function is the blessed creation path: it generates the id here with Attesto.Secret.generate/0 (256-bit CSPRNG), so a host cannot substitute a weak one. It mirrors Attesto.PresentationSession.create/3, which owns its session-id entropy the same way. Callers MUST use this rather than calling the store's put/1 with a self-chosen id.

Options:

  • :ttl — lifetime in seconds (default 300).

Returns {:ok, id}; build the retrieval URL from id and pass that URL to deep_link_by_reference/2.

to_query_value(offer)

@spec to_query_value(map()) :: String.t()

JSON-encode an offer for the credential_offer query parameter.