![EBSI Logo](https://ec.europa.eu/digital-building-blocks/sites/images/logo/default-space-logo.svg)

# Verifiable Credential Library

Create and verify EBSI-compliant W3C Verifiable Credentials in JWT format.

This library uses DIF's [did-jwt](https://github.com/decentralized-identity/did-jwt) under the hood and applies additional validation rules specific to EBSI. For more details, see [VC Framework](https://hub.ebsi.eu/vc-framework).

Notes:

- this library implements the [VC Data Model 1.1 specification](https://www.w3.org/TR/2022/REC-vc-data-model-20220303/).
- this library only supports [`2020-12` JSON Schemas](https://json-schema.org/draft/2020-12/json-schema-core.html).

## Table of Contents

1. [Installation](#installation)
2. [Usage](#usage)
3. [Axios config defaults](#axios-config-defaults)
4. [License](#license)

## Installation

Using npm:

```bash
npm i --save @cef-ebsi/verifiable-credential
```

Using yarn:

```bash
yarn add @cef-ebsi/verifiable-credential
```

## Usage

### Configuration

All the methods described below require a configuration object. The library exports a TypeScript interface that you can use to properly define the configuration object. Here's an example of such a configuration object:

```typescript
import type { EbsiEnvConfiguration } from "@cef-ebsi/verifiable-credential";

const config = {
  // List of trusted hosts
  hosts: ["api-pilot.ebsi.eu"],

  // Defines the URI scheme
  scheme: "ebsi",

  // Defines the network config
  network: {
    // Network component, as it appears in the URI
    name: "pilot",

    // Whether the network component is optional or not
    isOptional: false,
  },

  // The list of the supported services (with their version number)
  services: {
    "did-registry": "v5",
    "trusted-issuers-registry": "v5",
    "trusted-policies-registry": "v3",
    "trusted-schemas-registry": "v3",
  },
} as const satisfies EbsiEnvConfiguration;
```

### Creating JWTs

#### Prerequisites

In order to create a valid JWT, the issuer must either be:

1. a Legal Entity (`did:ebsi` method). In this case, the issuer must also be registered in the [DID Registry](https://hub.ebsi.eu/apis/pilot/did-registry/v5) and in the [Trusted Issuers Registry](https://hub.ebsi.eu/apis/pilot/trusted-issuers-registry/v5).
2. a Natural Person (`did:key` method).

#### Creating a Verifiable Credential as a Legal Entity

Create an `EbsiIssuer` object to sign JWTs:

```typescript
import type { EbsiIssuer } from "@cef-ebsi/verifiable-credential";
import { ES256KSigner } from "did-jwt";

const issuer = {
  did: "did:ebsi:zxaYaUtb8pvoAtYNWbKcveg",
  kid: "did:ebsi:zxaYaUtb8pvoAtYNWbKcveg#CHxYzOqt38Sx6YBfPYhiEdgcwzWk9ty7k0LBa6h70nc",
  alg: "ES256K",
  signer: ES256KSigner(privateKey),
} satisfies EbsiIssuer;
```

Specify a payload matching the `EbsiVerifiableAttestation` interface:

```typescript
import {
  createVerifiableCredentialJwt,
  type EbsiVerifiableAttestation,
} from "@cef-ebsi/verifiable-credential";

const vcPayload = {
  "@context": ["https://www.w3.org/2018/credentials/v1"],
  id: "urn:uuid:003a1dd8-a5d2-42ef-8182-e921c0a9f2cd",
  type: ["VerifiableCredential", "VerifiableAttestation"],
  issuer: "did:ebsi:zxaYaUtb8pvoAtYNWbKcveg",
  issuanceDate: "2021-11-01T00:00:00Z",
  validFrom: "2021-11-01T00:00:00Z",
  validUntil: "2050-11-01T00:00:00Z",
  expirationDate: "2031-11-30T00:00:00Z",
  issued: "2021-10-30T00:00:00Z",
  credentialSubject: {
    id: "did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbsNgeztBFXEB9FUZCoufTjXiTUZYKkcP36i2XAQCphfxBwvXG4dAaF6pdwhrMGyaLMC81fU5ECMnt4VgMQpwh3sn5vSbUpwoaTBME78noXJaTLgkCv5KkM6VgGTfWUjH8Z2",
  },
  credentialSchema: {
    id: "https://api-pilot.ebsi.eu/trusted-schemas-registry/v3/schemas/zDpWGUBenmqXzurskry9Nsk6vq2R8thh9VSeoRqguoyMD",
    type: "FullJsonSchemaValidator2021",
  },
  termsOfUse: {
    id: "https://api-pilot.ebsi.eu/trusted-issuers-registry/v5/issuers/did:ebsi:zxaYaUtb8pvoAtYNWbKcveg/attributes/b40fd9b404418a44d2d9911377a03130dde450eb546c755b5b80acd782902e6d",
    type: "IssuanceCertificate",
  },
} satisfies EbsiVerifiableAttestation;
```

Specify the options to validate the issuer and credential:

```typescript
import type { CreateVerifiableCredentialOptions } from "@cef-ebsi/verifiable-credential";

const options = {
  // OPTIONAL. Timeout after which the requests made by the library will fail. Default: 15 seconds
  // timeout: 15_000,
  // OPTIONAL. Determines whether to validate the Verifiable Credential payload or not.
  // Validation is active by default.
  // Note: even when skipValidation is set to true, the payload must be a valid EBSI Verifiable Attestation.
  // skipValidation: false,
  // OPTIONAL. Determines whether to validate the accreditations of the VC issuer or not.
  // Validation is active by default.
  // skipAccreditationsValidation: false,
  // OPTIONAL. Determines whether to validate the credential status or not.
  // Validation is active by default.
  // skipStatusValidation: false,
  // OPTIONAL. Determines whether to validate the credential subject or not
  // Validation is active by default.
  // skipCredentialSubjectValidation: false,
  // OPTIONAL. Unix timestamp. Optional comparison date. Default: current date and time.
  // validAt: Date.now(),
  // OPTIONAL. Credential subject. This parameter is mandatory if the payload's `credentialSubject` is an array.
  // It must correspond to one of the IDs in the payload's `credentialSubject` array.
  // sub: "did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbsNgeztBFXEB9FUZCoufTjXiTUZYKkcP36i2XAQCphfxBwvXG4dAaF6pdwhrMGyaLMC81fU5ECMnt4VgMQpwh3sn5vSbUpwoaTBME78noXJaTLgkCv5KkM6VgGTfWUjH8Z2",
  // OPTIONAL. Extra credentialSchema types. By default, the library only supports "FullJsonSchemaValidator2021" and "JsonSchema".
  // The library is not responsible for validating these extra types.
  // extraCredentialSchemaTypes: [],
} satisfies CreateVerifiableCredentialOptions;
```

Create a JWT by signing it with the previously configured issuer using the `createVerifiableCredentialJwt` function:

```typescript
const vcJwt = await createVerifiableCredentialJwt(
  vcPayload,
  issuer,
  config,
  options,
);
console.log(vcJwt);
// eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QiLCJraWQiOiJkaWQ6ZWJzaTp6eGFZYVV0Yjhwdm9BdFlOV2JLY3ZlZyNDSHhZek9xdDM4U3g2WUJmUFloaUVkZ2N3eldrOXR5N2swTEJhNmg3MG5jIn0.eyJqdGkiOiJ1cm46dXVpZDowMDNhMWRkOC1hNWQyLTQyZWYtODE4Mi1lOTIxYzBhOWYyY2QiLCJzdWIiOiJkaWQ6a2V5OnoyZG16RDgxY2dQeDhWa2k3SmJ1dU1tRllyV1BnWW95dHlrVVozZXlxaHQxajlLYnNOZ2V6dEJGWEVCOUZVWkNvdWZUalhpVFVaWUtrY1AzNmkyWEFRQ3BoZnhCd3ZYRzRkQWFGNnBkd2hyTUd5YUxNQzgxZlU1RUNNbnQ0VmdNUXB3aDNzbjV2U2JVcHdvYVRCTUU3OG5vWEphVExna0N2NUtrTTZWZ0dUZldVakg4WjIiLCJpc3MiOiJkaWQ6ZWJzaTp6eGFZYVV0Yjhwdm9BdFlOV2JLY3ZlZyIsIm5iZiI6MTYzNTcyNDgwMCwiZXhwIjoxOTUzNzYzMjAwLCJpYXQiOjE2MzU1NTIwMDAsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sImlkIjoidXJuOnV1aWQ6MDAzYTFkZDgtYTVkMi00MmVmLTgxODItZTkyMWMwYTlmMmNkIiwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIlZlcmlmaWFibGVBdHRlc3RhdGlvbiJdLCJpc3N1ZXIiOiJkaWQ6ZWJzaTp6eGFZYVV0Yjhwdm9BdFlOV2JLY3ZlZyIsImlzc3VhbmNlRGF0ZSI6IjIwMjEtMTEtMDFUMDA6MDA6MDBaIiwidmFsaWRGcm9tIjoiMjAyMS0xMS0wMVQwMDowMDowMFoiLCJ2YWxpZFVudGlsIjoiMjA1MC0xMS0wMVQwMDowMDowMFoiLCJleHBpcmF0aW9uRGF0ZSI6IjIwMzEtMTEtMzBUMDA6MDA6MDBaIiwiaXNzdWVkIjoiMjAyMS0xMC0zMFQwMDowMDowMFoiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDprZXk6ejJkbXpEODFjZ1B4OFZraTdKYnV1TW1GWXJXUGdZb3l0eWtVWjNleXFodDFqOUtic05nZXp0QkZYRUI5RlVaQ291ZlRqWGlUVVpZS2tjUDM2aTJYQVFDcGhmeEJ3dlhHNGRBYUY2cGR3aHJNR3lhTE1DODFmVTVFQ01udDRWZ01RcHdoM3NuNXZTYlVwd29hVEJNRTc4bm9YSmFUTGdrQ3Y1S2tNNlZnR1RmV1VqSDhaMiJ9LCJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiaHR0cHM6Ly9hcGktcGlsb3QuZWJzaS5ldS90cnVzdGVkLXNjaGVtYXMtcmVnaXN0cnkvdjMvc2NoZW1hcy96M01nVUZVa2I3MjJ1cTR4M2R2NXlBSm1uTm16REZlSzVVQzh4ODNRb2VMSk0iLCJ0eXBlIjoiRnVsbEpzb25TY2hlbWFWYWxpZGF0b3IyMDIxIn0sInRlcm1zT2ZVc2UiOnsiaWQiOiJodHRwczovL2FwaS1waWxvdC5lYnNpLmV1L3RydXN0ZWQtaXNzdWVycy1yZWdpc3RyeS92NS9pc3N1ZXJzL2RpZDplYnNpOnp4YVlhVXRiOHB2b0F0WU5XYktjdmVnL2F0dHJpYnV0ZXMvYjQwZmQ5YjQwNDQxOGE0NGQyZDk5MTEzNzdhMDMxMzBkZGU0NTBlYjU0NmM3NTViNWI4MGFjZDc4MjkwMmU2ZCIsInR5cGUiOiJJc3N1YW5jZUNlcnRpZmljYXRlIn19fQ.I8Im8azp0pSnM4z_jqGTTfYUg7K802POR2LTJftVfjYtMt82Cik2Z_KGOG7dHhx13R6VGVZKg9134ke1bKhtPw
```

#### Creating a Verifiable Credential as a Natural Person

Create an `EbsiIssuer` object to sign JWTs:

```typescript
import type { EbsiIssuer } from "@cef-ebsi/verifiable-credential";
import { ES256Signer } from "did-jwt";

const issuer = {
  did: "did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbsALgKJNpKj7U9jdYAHu3nxJ4fonckTBBGWELRpHJZ9dBKRzGebbNYTz6bWtaTM3zyyWnrkfb8w5owU1ECw2W7u1Rz2B5MgocMnnaa9wzDB3paEQvcgth9ErYawbNu9MLLc",
  kid: "did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbsALgKJNpKj7U9jdYAHu3nxJ4fonckTBBGWELRpHJZ9dBKRzGebbNYTz6bWtaTM3zyyWnrkfb8w5owU1ECw2W7u1Rz2B5MgocMnnaa9wzDB3paEQvcgth9ErYawbNu9MLLc#z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbsALgKJNpKj7U9jdYAHu3nxJ4fonckTBBGWELRpHJZ9dBKRzGebbNYTz6bWtaTM3zyyWnrkfb8w5owU1ECw2W7u1Rz2B5MgocMnnaa9wzDB3paEQvcgth9ErYawbNu9MLLc",
  alg: "ES256",
  signer: ES256Signer(privateKey),
} satisfies EbsiIssuer;
```

Specify a payload matching the `EbsiVerifiableAttestation` interface:

```typescript
import {
  createVerifiableCredentialJwt,
  type EbsiVerifiableAttestation,
} from "@cef-ebsi/verifiable-credential";

const vcPayload = {
  "@context": ["https://www.w3.org/2018/credentials/v1"],
  id: "urn:uuid:003a1dd8-a5d2-42ef-8182-e921c0a9f2cd",
  type: ["VerifiableCredential", "VerifiableAttestation"],
  issuer:
    "did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbsALgKJNpKj7U9jdYAHu3nxJ4fonckTBBGWELRpHJZ9dBKRzGebbNYTz6bWtaTM3zyyWnrkfb8w5owU1ECw2W7u1Rz2B5MgocMnnaa9wzDB3paEQvcgth9ErYawbNu9MLLc",
  issuanceDate: "2021-11-01T00:00:00Z",
  validFrom: "2021-11-01T00:00:00Z",
  validUntil: "2050-11-01T00:00:00Z",
  expirationDate: "2031-11-30T00:00:00Z",
  issued: "2021-10-30T00:00:00Z",
  credentialSubject: {
    id: "did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbrDgYJoht124ErdE8UUmbXR1gE7MnCayN6VZNuVMBZH2MfNVvbHJNYiHfWBWp3E3X5JnJYadaaL8pyJWh1a6JYpAkDKhUtefYDNZcv3BdDMvfp7auC1DVjs8ECRmKPPCw7r",
  },
  credentialSchema: {
    id: "https://api-pilot.ebsi.eu/trusted-schemas-registry/v3/schemas/zDpWGUBenmqXzurskry9Nsk6vq2R8thh9VSeoRqguoyMD",
    type: "FullJsonSchemaValidator2021",
  },
} satisfies EbsiVerifiableAttestation;
```

Specify the options to validate the issuer and credential:

```typescript
import type { CreateVerifiableCredentialOptions } from "@cef-ebsi/verifiable-credential";

const options = {
  timeout: 15_000,
} as const satisfies CreateVerifiableCredentialOptions;
```

Create a JWT by signing it with the previously configured issuer using the `createVerifiableCredentialJwt` function:

```typescript
const vcJwt = await createVerifiableCredentialJwt(
  vcPayload,
  issuer,
  config,
  options,
);
console.log(vcJwt);
// eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImRpZDprZXk6ejJkbXpEODFjZ1B4OFZraTdKYnV1TW1GWXJXUGdZb3l0eWtVWjNleXFodDFqOUtic0FMZ0tKTnBLajdVOWpkWUFIdTNueEo0Zm9uY2tUQkJHV0VMUnBISlo5ZEJLUnpHZWJiTllUejZiV3RhVE0zenl5V25ya2ZiOHc1b3dVMUVDdzJXN3UxUnoyQjVNZ29jTW5uYWE5d3pEQjNwYUVRdmNndGg5RXJZYXdiTnU5TUxMYyN6MmRtekQ4MWNnUHg4VmtpN0pidXVNbUZZcldQZ1lveXR5a1VaM2V5cWh0MWo5S2JzQUxnS0pOcEtqN1U5amRZQUh1M254SjRmb25ja1RCQkdXRUxScEhKWjlkQktSekdlYmJOWVR6NmJXdGFUTTN6eXlXbnJrZmI4dzVvd1UxRUN3Mlc3dTFSejJCNU1nb2NNbm5hYTl3ekRCM3BhRVF2Y2d0aDlFcllhd2JOdTlNTExjIn0.eyJqdGkiOiJ1cm46dXVpZDowMDNhMWRkOC1hNWQyLTQyZWYtODE4Mi1lOTIxYzBhOWYyY2QiLCJzdWIiOiJkaWQ6a2V5OnoyZG16RDgxY2dQeDhWa2k3SmJ1dU1tRllyV1BnWW95dHlrVVozZXlxaHQxajlLYnJEZ1lKb2h0MTI0RXJkRThVVW1iWFIxZ0U3TW5DYXlONlZaTnVWTUJaSDJNZk5WdmJISk5ZaUhmV0JXcDNFM1g1Sm5KWWFkYWFMOHB5SldoMWE2SllwQWtES2hVdGVmWUROWmN2M0JkRE12ZnA3YXVDMURWanM4RUNSbUtQUEN3N3IiLCJpc3MiOiJkaWQ6a2V5OnoyZG16RDgxY2dQeDhWa2k3SmJ1dU1tRllyV1BnWW95dHlrVVozZXlxaHQxajlLYnNBTGdLSk5wS2o3VTlqZFlBSHUzbnhKNGZvbmNrVEJCR1dFTFJwSEpaOWRCS1J6R2ViYk5ZVHo2Yld0YVRNM3p5eVducmtmYjh3NW93VTFFQ3cyVzd1MVJ6MkI1TWdvY01ubmFhOXd6REIzcGFFUXZjZ3RoOUVyWWF3Yk51OU1MTGMiLCJuYmYiOjE2MzU3MjQ4MDAsImV4cCI6MTk1Mzc2MzIwMCwiaWF0IjoxNjM1NTUyMDAwLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJpZCI6InVybjp1dWlkOjAwM2ExZGQ4LWE1ZDItNDJlZi04MTgyLWU5MjFjMGE5ZjJjZCIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJWZXJpZmlhYmxlQXR0ZXN0YXRpb24iXSwiaXNzdWVyIjoiZGlkOmtleTp6MmRtekQ4MWNnUHg4VmtpN0pidXVNbUZZcldQZ1lveXR5a1VaM2V5cWh0MWo5S2JzQUxnS0pOcEtqN1U5amRZQUh1M254SjRmb25ja1RCQkdXRUxScEhKWjlkQktSekdlYmJOWVR6NmJXdGFUTTN6eXlXbnJrZmI4dzVvd1UxRUN3Mlc3dTFSejJCNU1nb2NNbm5hYTl3ekRCM3BhRVF2Y2d0aDlFcllhd2JOdTlNTExjIiwiaXNzdWFuY2VEYXRlIjoiMjAyMS0xMS0wMVQwMDowMDowMFoiLCJ2YWxpZEZyb20iOiIyMDIxLTExLTAxVDAwOjAwOjAwWiIsInZhbGlkVW50aWwiOiIyMDUwLTExLTAxVDAwOjAwOjAwWiIsImV4cGlyYXRpb25EYXRlIjoiMjAzMS0xMS0zMFQwMDowMDowMFoiLCJpc3N1ZWQiOiIyMDIxLTEwLTMwVDAwOjAwOjAwWiIsImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmtleTp6MmRtekQ4MWNnUHg4VmtpN0pidXVNbUZZcldQZ1lveXR5a1VaM2V5cWh0MWo5S2JyRGdZSm9odDEyNEVyZEU4VVVtYlhSMWdFN01uQ2F5TjZWWk51Vk1CWkgyTWZOVnZiSEpOWWlIZldCV3AzRTNYNUpuSllhZGFhTDhweUpXaDFhNkpZcEFrREtoVXRlZllETlpjdjNCZERNdmZwN2F1QzFEVmpzOEVDUm1LUFBDdzdyIn0sImNyZWRlbnRpYWxTY2hlbWEiOnsiaWQiOiJodHRwczovL2FwaS1waWxvdC5lYnNpLmV1L3RydXN0ZWQtc2NoZW1hcy1yZWdpc3RyeS92My9zY2hlbWFzL3ozTWdVRlVrYjcyMnVxNHgzZHY1eUFKbW5ObXpERmVLNVVDOHg4M1FvZUxKTSIsInR5cGUiOiJGdWxsSnNvblNjaGVtYVZhbGlkYXRvcjIwMjEifX19.BArwMltlr61I9Pg_SOtewJJF6IfDbo0I2RHFQtM7JPQ8ZWjmPuIU2sH7SnT1yTPC_xWFvmG363U6DNFChnpvGQ
```

### Verifying JWTs

#### Prerequisites

Verify a VC JWT using the `verifyCredentialJwt` function:

```typescript
import {
  verifyCredentialJwt,
  type VerifyCredentialOptions,
} from "@cef-ebsi/verifiable-credential";

const vcJwt =
  "eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QiLCJraWQiOiJkaWQ6ZWJzaTp6eGFZYVV0Yjhwdm9BdFlOV2JLY3ZlZyNDSHhZek9xdDM4U3g2WUJmUFloaUVkZ2N3eldrOXR5N2swTEJhNmg3MG5jIn0.eyJqdGkiOiJ1cm46dXVpZDowMDNhMWRkOC1hNWQyLTQyZWYtODE4Mi1lOTIxYzBhOWYyY2QiLCJzdWIiOiJkaWQ6a2V5OnoyZG16RDgxY2dQeDhWa2k3SmJ1dU1tRllyV1BnWW95dHlrVVozZXlxaHQxajlLYnNOZ2V6dEJGWEVCOUZVWkNvdWZUalhpVFVaWUtrY1AzNmkyWEFRQ3BoZnhCd3ZYRzRkQWFGNnBkd2hyTUd5YUxNQzgxZlU1RUNNbnQ0VmdNUXB3aDNzbjV2U2JVcHdvYVRCTUU3OG5vWEphVExna0N2NUtrTTZWZ0dUZldVakg4WjIiLCJpc3MiOiJkaWQ6ZWJzaTp6eGFZYVV0Yjhwdm9BdFlOV2JLY3ZlZyIsIm5iZiI6MTYzNTcyNDgwMCwiZXhwIjoxOTUzNzYzMjAwLCJpYXQiOjE2MzU1NTIwMDAsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sImlkIjoidXJuOnV1aWQ6MDAzYTFkZDgtYTVkMi00MmVmLTgxODItZTkyMWMwYTlmMmNkIiwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIlZlcmlmaWFibGVBdHRlc3RhdGlvbiJdLCJpc3N1ZXIiOiJkaWQ6ZWJzaTp6eGFZYVV0Yjhwdm9BdFlOV2JLY3ZlZyIsImlzc3VhbmNlRGF0ZSI6IjIwMjEtMTEtMDFUMDA6MDA6MDBaIiwidmFsaWRGcm9tIjoiMjAyMS0xMS0wMVQwMDowMDowMFoiLCJ2YWxpZFVudGlsIjoiMjA1MC0xMS0wMVQwMDowMDowMFoiLCJleHBpcmF0aW9uRGF0ZSI6IjIwMzEtMTEtMzBUMDA6MDA6MDBaIiwiaXNzdWVkIjoiMjAyMS0xMC0zMFQwMDowMDowMFoiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDprZXk6ejJkbXpEODFjZ1B4OFZraTdKYnV1TW1GWXJXUGdZb3l0eWtVWjNleXFodDFqOUtic05nZXp0QkZYRUI5RlVaQ291ZlRqWGlUVVpZS2tjUDM2aTJYQVFDcGhmeEJ3dlhHNGRBYUY2cGR3aHJNR3lhTE1DODFmVTVFQ01udDRWZ01RcHdoM3NuNXZTYlVwd29hVEJNRTc4bm9YSmFUTGdrQ3Y1S2tNNlZnR1RmV1VqSDhaMiJ9LCJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiaHR0cHM6Ly9hcGktcGlsb3QuZWJzaS5ldS90cnVzdGVkLXNjaGVtYXMtcmVnaXN0cnkvdjMvc2NoZW1hcy96M01nVUZVa2I3MjJ1cTR4M2R2NXlBSm1uTm16REZlSzVVQzh4ODNRb2VMSk0iLCJ0eXBlIjoiRnVsbEpzb25TY2hlbWFWYWxpZGF0b3IyMDIxIn0sInRlcm1zT2ZVc2UiOnsiaWQiOiJodHRwczovL2FwaS1waWxvdC5lYnNpLmV1L3RydXN0ZWQtaXNzdWVycy1yZWdpc3RyeS92NS9pc3N1ZXJzL2RpZDplYnNpOnp4YVlhVXRiOHB2b0F0WU5XYktjdmVnL2F0dHJpYnV0ZXMvYjQwZmQ5YjQwNDQxOGE0NGQyZDk5MTEzNzdhMDMxMzBkZGU0NTBlYjU0NmM3NTViNWI4MGFjZDc4MjkwMmU2ZCIsInR5cGUiOiJJc3N1YW5jZUNlcnRpZmljYXRlIn19fQ.I8Im8azp0pSnM4z_jqGTTfYUg7K802POR2LTJftVfjYtMt82Cik2Z_KGOG7dHhx13R6VGVZKg9134ke1bKhtPw";

const options = {
  // OPTIONAL. Timeout after which the requests made by the library will fail. Default: 15 seconds
  // timeout: 15_000,
  // OPTIONAL. Determines whether the JSON to JWT transformation will remove the original fields from the input payload.
  // Default: true
  // removeOriginalFields: true,
  // OPTIONAL. Determines whether to validate the accreditations of the VC issuer or not.
  // Validation is active by default.
  // skipAccreditationsValidation: false,
  // OPTIONAL. Determines whether to validate the credential status or not.
  // Validation is active by default.
  // skipStatusValidation: false,
  // OPTIONAL. Determines whether to validate the credential subject or not
  // Validation is active by default.
  // skipCredentialSubjectValidation: false,
  // OPTIONAL. Unix timestamp. Optional comparison date. Default: current date and time.
  // For the JWT to be valid, `nbf` ≤ `validAt` ≤ `exp`.
  // validAt: 1686048193,
  // OPTIONAL. Determines whether or not to validate the issuer's accreditations when `termsOfUse` is missing. Default: false
  // validateAccreditationWithoutTermsOfUse: false,
  // OPTIONAL. Credential subject. This parameter is mandatory if the payload's `credentialSubject` is an array.
  // It must correspond to one of the IDs in the payload's `credentialSubject` array.
  // sub: "did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbsNgeztBFXEB9FUZCoufTjXiTUZYKkcP36i2XAQCphfxBwvXG4dAaF6pdwhrMGyaLMC81fU5ECMnt4VgMQpwh3sn5vSbUpwoaTBME78noXJaTLgkCv5KkM6VgGTfWUjH8Z2",
  // OPTIONAL. Extra credentialSchema types. By default, the library only supports "FullJsonSchemaValidator2021" and "JsonSchema".
  // The library is not responsible for validating these extra types.
  // extraCredentialSchemaTypes: [],
} satisfies VerifyCredentialOptions;

const verifiedVc = await verifyCredentialJwt(vcJwt, config, options);

console.log(verifiedVc);
/*
{
  '@context': [ 'https://www.w3.org/2018/credentials/v1' ],
  id: 'urn:uuid:003a1dd8-a5d2-42ef-8182-e921c0a9f2cd',
  type: [ 'VerifiableCredential', 'VerifiableAttestation' ],
  issuer: 'did:ebsi:zxaYaUtb8pvoAtYNWbKcveg',
  issuanceDate: '2021-11-01T00:00:00Z',
  validFrom: '2021-11-01T00:00:00Z',
  validUntil: '2050-11-01T00:00:00Z',
  expirationDate: '2031-11-30T00:00:00Z',
  issued: '2021-10-30T00:00:00Z',
  credentialSubject: {
    id: 'did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbsNgeztBFXEB9FUZCoufTjXiTUZYKkcP36i2XAQCphfxBwvXG4dAaF6pdwhrMGyaLMC81fU5ECMnt4VgMQpwh3sn5vSbUpwoaTBME78noXJaTLgkCv5KkM6VgGTfWUjH8Z2'
  },
  credentialSchema: {
    id: 'https://api-pilot.ebsi.eu/trusted-schemas-registry/v3/schemas/zDpWGUBenmqXzurskry9Nsk6vq2R8thh9VSeoRqguoyMD',
    type: 'FullJsonSchemaValidator2021'
  },
  termsOfUse: {
    id: 'https://api-pilot.ebsi.eu/trusted-issuers-registry/v5/issuers/did:ebsi:zxaYaUtb8pvoAtYNWbKcveg/attributes/b40fd9b404418a44d2d9911377a03130dde450eb546c755b5b80acd782902e6d',
    type: 'IssuanceCertificate'
  }
}
*/
```

#### Verification process

The verification of the credentials is performed using the following process:

1. **Header**

- The `alg` property must be `ES256`, `ES256K`, or `EdDSA`.
- The `kid` property must contain the DID of the issuer and the fragment identifier allowing to know which verification method to use.

2. **Basic properties in the payload**

- The payload must contain a `vc` property containing an EBSI Verifiable Attestation.
- The `iss` property must match the VC issuer `vc.issuer`.
- The `sub` property must match the VC credential subject `vc.credentialSubject.id`.
- The `jti` property must match the VC ID `vc.id`.
- The `iat` property must refer to the date in VC issued `vc.issued`.
- The `nbf` property must refer to the date in VC valid from `vc.validFrom`.
- The `exp` property mush refer to date in VC expiration date `vc.expirationDate` in the case it is defined.

3. **Signature**

- The signature must be validated against the verification method from the DID document corresponding to the VC JWT `kid` header.

4. **Extra validations to the VC payload**

- Validation of `@context`.
- Validation of `type`.
- Validation of dates.

5. **Issuer in Trusted Issuers Registry**

If the issuer is a Legal Entity:

- The issuer must be registered in the Trusted Issuers Registry.

Additionally, if the VC is an attestation with accreditation:

- The VC must contain a `termsOfUse` property pointing to the accreditations of the issuer.
- The issuer must be accredited to issue the types present in the credential.

6. **Credential subject**

- The credential subject must be a valid DID, using either the `did:ebsi` v1 method (Legal Entity) or the `did:key` method (Natural Person).

7. **Credential schema**

- The VC credential schema(s) must link to valid EBSI credential schemas, registered in the [Trusted Schemas Registry](https://hub.ebsi.eu/apis/pilot/trusted-schemas-registry/v3).
- The credential payload is validated against the aforementioned JSON Schemas.

## Axios config defaults

The library uses the global [axios](https://axios-http.com) instance to make HTTP requests. If needed, you can specify config defaults. More information on axios configuration can be found [here](https://axios-http.com/docs/config_defaults).

For instance, if you want to override the default HTTPS agent with a custom agent using [https-proxy-agent](https://www.npmjs.com/package/https-proxy-agent), you can do it like so:

```js
import axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";

axios.defaults.httpsAgent = new HttpsProxyAgent("http://168.63.76.32:3128");
```

## License

Copyright (C) 2025 European Union

This program is free software: you can redistribute it and/or modify it under the terms of the EUROPEAN UNION PUBLIC LICENCE v. 1.2 as published by the European Union.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the EUROPEAN UNION PUBLIC LICENCE v. 1.2 for further details.

You should have received a copy of the EUROPEAN UNION PUBLIC LICENCE v. 1.2. along with this program. If not, see <https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12>.
