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
Types
@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() ]
@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
@type verify_opts() :: [ now: DateTime.t() | non_neg_integer(), issuer: String.t(), accepted_algs: [Attesto.SigningAlg.alg()] ]
Functions
@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/3;
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.
@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.