Attesto.JwtVc (Attesto v1.12.0)

Copy Markdown View Source

W3C Verifiable Credentials Data Model 1.1 credentials encoded as JWTs, corresponding to the OID4VCI jwt_vc_json Credential Format.

A JWT VC is a compact JWS whose payload contains a vc object alongside registered JWT claims. This module emits the following representation:

{
  "iss" => issuer,
  "sub" => subject,
  "nbf" => not_before,
  "exp" => expires_at,
  "iat" => issued_at,
  "jti" => credential_id,
  "cnf" => confirmation,
  "vc" => %{
    "@context" => ["https://www.w3.org/2018/credentials/v1"],
    "type" => ["VerifiableCredential", credential_type],
    "credentialSubject" => credential_claims,
    "issuer" => issuer
  }
}

W3C VC Data Model 1.1 section 6.3.1 defines the JWT mapping: vc is required; iss, sub, nbf, exp, and jti represent the corresponding VC issuer, subject id, issuance date, expiration date, and credential id. iat is also emitted as the actual signing time. The JOSE typ is JWT, as required by that mapping when the header is present. OID4VCI draft 17 Appendix A.1.1 calls this non-JSON-LD JWS representation jwt_vc_json and carries the compact JWT directly in the Credential Response.

Holder binding is represented by an optional RFC 7800 cnf claim. This module binds the confirmation material into the issuer signature and returns it after verification; proving possession of the referenced private key is a separate presentation-protocol step.

Issuance accepts either an Attesto.Keystore module or a private PEM. Verification is conn-free and uses only caller-supplied trusted issuer JWKS. It requires all registered claims this module emits, rejects malformed or absent temporal claims, and verifies the signature before returning any credential data.

Summary

Functions

Issue a signed W3C JWT VC.

Verify a W3C JWT VC against trusted issuer keys.

Types

issue_opts()

@type issue_opts() :: [
  iss: String.t(),
  sub: String.t(),
  claims: map(),
  credential_subject: map(),
  context: [String.t() | map()],
  type: [String.t()],
  iat: non_neg_integer(),
  nbf: non_neg_integer(),
  exp: non_neg_integer(),
  lifetime: pos_integer(),
  jti: String.t(),
  cnf: map(),
  now: DateTime.t() | non_neg_integer(),
  keystore: module(),
  pem: String.t(),
  alg: Attesto.SigningAlg.alg(),
  kid: String.t()
]

issue_source()

@type issue_source() :: keyword() | module() | String.t()

verified()

@type verified() :: %{
  claims: map(),
  vc: map(),
  cnf: map() | nil,
  iss: String.t(),
  sub: String.t(),
  jwt_claims: map()
}

verify_error()

@type verify_error() ::
  :invalid_credential
  | :unsupported_critical_header
  | :invalid_typ
  | :unsupported_alg
  | :invalid_signature
  | :invalid_claims
  | :invalid_vc
  | :invalid_issuer
  | :invalid_cnf
  | :expired
  | :not_yet_valid

verify_opts()

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

Functions

issue(source, opts \\ [])

@spec issue(issue_source(), issue_opts()) :: String.t()

Issue a signed W3C JWT VC.

The first argument may be a keyword list containing all options, a keystore module, or a private signing-key PEM. When it is a keyword list, pass exactly one of :keystore and :pem. A keystore uses Attesto.JWS.sign_current; a PEM derives its algorithm and default kid from the same parsed key.

Required options are :iss and :sub. Subject claims can be supplied as :claims (matching Attesto.SdJwtVc) or :credential_subject; they default to an empty map. :context defaults to the VC 1.1 base context and :type defaults to ["VerifiableCredential"].

iat and nbf default to :now; exp defaults to one hour after iat (or the positive :lifetime); and jti defaults to a random UUID URN. Pass :cnf, for example %{"jwk" => holder_public_jwk}, to bind the credential to holder key material under RFC 7800.

Returns the compact JWT string. Invalid issuer input is a programming or configuration error and raises ArgumentError, matching the issuance style of Attesto.SdJwtVc.

verify(jwt, trusted_jwks, opts \\ [])

@spec verify(String.t(), map() | [map()], verify_opts()) ::
  {:ok, verified()} | {:error, verify_error()}

Verify a W3C JWT VC against trusted issuer keys.

trusted_jwks may be an RFC 7517 JWK Set, a single public JWK map, or a list of public JWK maps. Verification requires typ: JWT, rejects critical JOSE extensions, binds the algorithm to each trusted key, and then validates the W3C vc object and all registered claims emitted by issue/2.

Temporal validation is fail-closed: iat, nbf, and exp must all be non-negative integer NumericDates; exp must be strictly in the future; and iat/nbf may be no more than 60 seconds ahead of the verifier clock. Pass :issuer to additionally pin iss to an expected identifier. The nested VC issuer always has to agree with iss.

On success, :claims is the VC credentialSubject, :vc is the complete nested VC object, :cnf is the optional holder confirmation object, and :jwt_claims retains the complete signed JWT payload.