# `Attesto.JwtVc`
[🔗](https://github.com/XukuLLC/attesto/blob/v2.1.0/lib/attesto/jwt_vc.ex#L1)

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.

# `issue_opts`

```elixir
@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`

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

# `verified`

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

# `verify_error`

```elixir
@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`

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

# `issue`

```elixir
@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`

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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
