Skip to main content
European CommissionEBSI European Blockchain

Verifiable Credential

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

This library extends the @transmute/json-web-signature and @transmute/vc.js libraries by applying additional validation rules. For more details, see EBSI Verifiable Credentials Playbook.

Note: this library only supports 2020-12 JSON Schemas.

Installation

Using npm:

$ npm i --save @cef-ebsi/verifiable-credential

Using yarn:

$ yarn add @cef-ebsi/verifiable-credential

Usage

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 and in the Trusted Issuers Registry.
  2. a Natural Person (did:key method).

Create an EbsiIssuer object to sign JWTs:

import type { EbsiIssuer } from "@cef-ebsi/verifiable-credential";

const issuer: EbsiIssuer = {
did: "did:ebsi:zz7XsC9ixAXuZecoD9sZEM1",
kid: "did:ebsi:zz7XsC9ixAXuZecoD9sZEM1#kLMMwMvg_MAJG6tPqUUARrNQhqxUSPGvBg2smCrL_c0",
alg: "ES256K",
publicKeyJwk: {
kty: "EC",
crv: "secp256k1",
x: "3XhnCJvr8qkrahOiTXXdHCcbsj238CSOxwzR0_CrOy0",
y: "TL7WB16TbDF45Kb2ozlBqm9zItGQcy7SfZFx6l0O3fg",
},
privateKeyJwk: {
kty: "EC",
crv: "secp256k1",
x: "3XhnCJvr8qkrahOiTXXdHCcbsj238CSOxwzR0_CrOy0",
y: "TL7WB16TbDF45Kb2ozlBqm9zItGQcy7SfZFx6l0O3fg",
d: "<ECC private key>",
},
};

Specify a payload matching the EbsiVerifiableAttestation interface:

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

const vcPayload: EbsiVerifiableAttestation = {
"@context": ["https://www.w3.org/2018/credentials/v1"],
id: "urn:did:123456",
type: ["VerifiableCredential", "VerifiableAttestation", "VerifiableId"],
issuer: "did:ebsi:zz7XsC9ixAXuZecoD9sZEM1",
issuanceDate: "2021-11-01T00:00:00Z",
validFrom: "2021-11-01T00:00:00Z",
issued: "2021-10-30T00:00:00Z",
credentialSubject: {
id: "did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbrDt4zxXoDrBWYFiATYZ8G9JMeEXC7Kki24fbTwtsJbGe5qcbkYFunSzcDokMRmj8UJ1PbdCGh33mf97K3To89bMzd15qrYq3VkDztoZqfmujkJVpvTbqoXWXqxmzNDbvMJ",
personalIdentifier: "IT/DE/1234",
familyName: "Castafiori",
firstName: "Bianca",
dateOfBirth: "1930-10-01",
},
credentialSchema: {
id: "https://api-test.ebsi.eu/trusted-schemas-registry/v2/schemas/z3MgUFUkb722uq4x3dv5yAJmnNmzDFeK5UC8x83QoeLJM",
type: "FullJsonSchemaValidator2021",
},
expirationDate: "2031-11-30T00:00:00Z",
termsOfUse: {
id: "https://api-test.ebsi.eu/trusted-issuers-registry/v4/issuers/did:ebsi:zz7XsC9ixAXuZecoD9sZEM1/attributes/7201d95fef05f72667f5454c2192da2aa30d9e052eeddea7651b47718d6f31b0",
type: "IssuanceCertificate",
},
};

Specify the options to validate the issuer and credential:

const options = {
// REQUIRED. EBSI URI Authority ([userinfo "@"] host [":" port])
ebsiAuthority: "api-test.ebsi.eu",
};

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

const vcJwt = await createVerifiableCredentialJwt(vcPayload, issuer, options);
console.log(vcJwt);
// eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QiLCJraWQiOiJkaWQ6ZWJzaTp6ejdYc0M5aXhBWHVaZWNvRDlzWkVNMSNrTE1Nd012Z19NQUpHNnRQcVVVQVJyTlFocXhVU1BHdkJnMnNtQ3JMX2MwIn0.eyJqdGkiOiJ1cm46ZGlkOjEyMzQ1NiIsInN1YiI6ImRpZDprZXk6ejJkbXpEODFjZ1B4OFZraTdKYnV1TW1GWXJXUGdZb3l0eWtVWjNleXFodDFqOUtickR0NHp4WG9EckJXWUZpQVRZWjhHOUpNZUVYQzdLa2kyNGZiVHd0c0piR2U1cWNia1lGdW5TemNEb2tNUm1qOFVKMVBiZENHaDMzbWY5N0szVG84OWJNemQxNXFyWXEzVmtEenRvWnFmbXVqa0pWcHZUYnFvWFdYcXhtek5EYnZNSiIsImlzcyI6ImRpZDplYnNpOnp6N1hzQzlpeEFYdVplY29EOXNaRU0xIiwibmJmIjoxNjM1NzI0ODAwLCJleHAiOjE5NTM3NjMyMDAsImlhdCI6MTYzNTU1MjAwMCwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiXSwiaWQiOiJ1cm46ZGlkOjEyMzQ1NiIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJWZXJpZmlhYmxlQXR0ZXN0YXRpb24iLCJWZXJpZmlhYmxlSWQiXSwiaXNzdWVyIjoiZGlkOmVic2k6eno3WHNDOWl4QVh1WmVjb0Q5c1pFTTEiLCJpc3N1YW5jZURhdGUiOiIyMDIxLTExLTAxVDAwOjAwOjAwWiIsInZhbGlkRnJvbSI6IjIwMjEtMTEtMDFUMDA6MDA6MDBaIiwiaXNzdWVkIjoiMjAyMS0xMC0zMFQwMDowMDowMFoiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDprZXk6ejJkbXpEODFjZ1B4OFZraTdKYnV1TW1GWXJXUGdZb3l0eWtVWjNleXFodDFqOUtickR0NHp4WG9EckJXWUZpQVRZWjhHOUpNZUVYQzdLa2kyNGZiVHd0c0piR2U1cWNia1lGdW5TemNEb2tNUm1qOFVKMVBiZENHaDMzbWY5N0szVG84OWJNemQxNXFyWXEzVmtEenRvWnFmbXVqa0pWcHZUYnFvWFdYcXhtek5EYnZNSiIsInBlcnNvbmFsSWRlbnRpZmllciI6IklUL0RFLzEyMzQiLCJmYW1pbHlOYW1lIjoiQ2FzdGFmaW9yaSIsImZpcnN0TmFtZSI6IkJpYW5jYSIsImRhdGVPZkJpcnRoIjoiMTkzMC0xMC0wMSJ9LCJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiaHR0cHM6Ly9hcGktdGVzdC5lYnNpLmV1L3RydXN0ZWQtc2NoZW1hcy1yZWdpc3RyeS92Mi9zY2hlbWFzL3ozTWdVRlVrYjcyMnVxNHgzZHY1eUFKbW5ObXpERmVLNVVDOHg4M1FvZUxKTSIsInR5cGUiOiJGdWxsSnNvblNjaGVtYVZhbGlkYXRvcjIwMjEifSwiZXhwaXJhdGlvbkRhdGUiOiIyMDMxLTExLTMwVDAwOjAwOjAwWiIsInRlcm1zT2ZVc2UiOnsiaWQiOiJodHRwczovL2FwaS10ZXN0LmVic2kuZXUvdHJ1c3RlZC1pc3N1ZXJzLXJlZ2lzdHJ5L3Y0L2lzc3VlcnMvZGlkOmVic2k6eno3WHNDOWl4QVh1WmVjb0Q5c1pFTTEvYXR0cmlidXRlcy83MjAxZDk1ZmVmMDVmNzI2NjdmNTQ1NGMyMTkyZGEyYWEzMGQ5ZTA1MmVlZGRlYTc2NTFiNDc3MThkNmYzMWIwIiwidHlwZSI6Iklzc3VhbmNlQ2VydGlmaWNhdGUifX19.RHS1ftcvhn-6UWIMyqXnYKyQIDamVY6xs2ejUDZMxRVucwa7TizvAF0C0XhkFfpay43hWtYdH1wJqQtpExHqjQ

Creating a Verifiable Credential as a Natural Person

Create an EbsiIssuer object to sign JWTs:

import type { EbsiIssuer } from "@cef-ebsi/verifiable-credential";

const issuer: EbsiIssuer = {
did: "did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbsU7X4QCja3FnpF9UQKFFJUxwg5beEhbuyTtG1Phs5dddBjfftya2pXCoVjk9UUTUwP7yKkxU7tGURWWdsN1wYgRzfgmCZYcMu1p9H8riieESov3nNn4mAgYW3NbLm6VrfE",
kid: "did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbsU7X4QCja3FnpF9UQKFFJUxwg5beEhbuyTtG1Phs5dddBjfftya2pXCoVjk9UUTUwP7yKkxU7tGURWWdsN1wYgRzfgmCZYcMu1p9H8riieESov3nNn4mAgYW3NbLm6VrfE#z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbsU7X4QCja3FnpF9UQKFFJUxwg5beEhbuyTtG1Phs5dddBjfftya2pXCoVjk9UUTUwP7yKkxU7tGURWWdsN1wYgRzfgmCZYcMu1p9H8riieESov3nNn4mAgYW3NbLm6VrfE",
alg: "ES256",
publicKeyJwk: {
kty: "EC",
x: "qfv89cqPOFFztdvmFFdE7TdYuh-p3reksHQ3-qQkotg",
y: "R5dqivoDlWjvEDVYvxVGveQtpkiIMh2sFYGYVEgxQQQ",
crv: "P-256",
},
privateKeyJwk: {
kty: "EC",
x: "qfv89cqPOFFztdvmFFdE7TdYuh-p3reksHQ3-qQkotg",
y: "R5dqivoDlWjvEDVYvxVGveQtpkiIMh2sFYGYVEgxQQQ",
crv: "P-256",
d: "4Hc8_Un61mb0McNdymtUkHn4xApT5XKr9YDg4Mk8vJg",
},
};

Specify a payload matching the EbsiVerifiableAttestation interface:

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

const vcPayload: EbsiVerifiableAttestation = {
"@context": ["https://www.w3.org/2018/credentials/v1"],
id: "urn:did:123456",
type: ["VerifiableCredential", "VerifiableAttestation", "VerifiableId"],
issuer:
"did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbsU7X4QCja3FnpF9UQKFFJUxwg5beEhbuyTtG1Phs5dddBjfftya2pXCoVjk9UUTUwP7yKkxU7tGURWWdsN1wYgRzfgmCZYcMu1p9H8riieESov3nNn4mAgYW3NbLm6VrfE",
issuanceDate: "2021-11-01T00:00:00Z",
validFrom: "2021-11-01T00:00:00Z",
issued: "2021-10-30T00:00:00Z",
credentialSubject: {
id: "did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbrzKGZpVaXjYmKmRZucEYujY7b1gY4S4of6yZgh5vd34fi1DLGi6G5MmhJHaw3hvXHucxi4s7jDhBaE7zC4Dam8ZPf1Q5j3hZ4CANLPcKQR1dQEr81LhMXR4eEsb2V3D7XS",
personalIdentifier: "IT/DE/1234",
familyName: "Castafiori",
firstName: "Bianca",
dateOfBirth: "1930-10-01",
},
credentialSchema: {
id: "https://api-test.ebsi.eu/trusted-schemas-registry/v2/schemas/z3MgUFUkb722uq4x3dv5yAJmnNmzDFeK5UC8x83QoeLJM",
type: "FullJsonSchemaValidator2021",
},
expirationDate: "2031-11-30T00:00:00Z",
};

Specify the options to validate the issuer and credential:

const options = {
// REQUIRED. EBSI URI Authority ([userinfo "@"] host [":" port])
ebsiAuthority: "api-test.ebsi.eu",
};

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

const vcJwt = await createVerifiableCredentialJwt(vcPayload, issuer, options);
console.log(vcJwt);
// eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImRpZDprZXk6ejJkbXpEODFjZ1B4OFZraTdKYnV1TW1GWXJXUGdZb3l0eWtVWjNleXFodDFqOUtic1U3WDRRQ2phM0ZucEY5VVFLRkZKVXh3ZzViZUVoYnV5VHRHMVBoczVkZGRCamZmdHlhMnBYQ29Wams5VVVUVXdQN3lLa3hVN3RHVVJXV2RzTjF3WWdSemZnbUNaWWNNdTFwOUg4cmlpZUVTb3Yzbk5uNG1BZ1lXM05iTG02VnJmRSN6MmRtekQ4MWNnUHg4VmtpN0pidXVNbUZZcldQZ1lveXR5a1VaM2V5cWh0MWo5S2JzVTdYNFFDamEzRm5wRjlVUUtGRkpVeHdnNWJlRWhidXlUdEcxUGhzNWRkZEJqZmZ0eWEycFhDb1ZqazlVVVRVd1A3eUtreFU3dEdVUldXZHNOMXdZZ1J6ZmdtQ1pZY011MXA5SDhyaWllRVNvdjNuTm40bUFnWVczTmJMbTZWcmZFIn0.eyJqdGkiOiJ1cm46ZGlkOjEyMzQ1NiIsInN1YiI6ImRpZDprZXk6ejJkbXpEODFjZ1B4OFZraTdKYnV1TW1GWXJXUGdZb3l0eWtVWjNleXFodDFqOUticnpLR1pwVmFYalltS21SWnVjRVl1alk3YjFnWTRTNG9mNnlaZ2g1dmQzNGZpMURMR2k2RzVNbWhKSGF3M2h2WEh1Y3hpNHM3akRoQmFFN3pDNERhbThaUGYxUTVqM2haNENBTkxQY0tRUjFkUUVyODFMaE1YUjRlRXNiMlYzRDdYUyIsImlzcyI6ImRpZDprZXk6ejJkbXpEODFjZ1B4OFZraTdKYnV1TW1GWXJXUGdZb3l0eWtVWjNleXFodDFqOUtic1U3WDRRQ2phM0ZucEY5VVFLRkZKVXh3ZzViZUVoYnV5VHRHMVBoczVkZGRCamZmdHlhMnBYQ29Wams5VVVUVXdQN3lLa3hVN3RHVVJXV2RzTjF3WWdSemZnbUNaWWNNdTFwOUg4cmlpZUVTb3Yzbk5uNG1BZ1lXM05iTG02VnJmRSIsIm5iZiI6MTYzNTcyNDgwMCwiZXhwIjoxOTUzNzYzMjAwLCJpYXQiOjE2MzU1NTIwMDAsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sImlkIjoidXJuOmRpZDoxMjM0NTYiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVmVyaWZpYWJsZUF0dGVzdGF0aW9uIiwiVmVyaWZpYWJsZUlkIl0sImlzc3VlciI6ImRpZDprZXk6ejJkbXpEODFjZ1B4OFZraTdKYnV1TW1GWXJXUGdZb3l0eWtVWjNleXFodDFqOUtic1U3WDRRQ2phM0ZucEY5VVFLRkZKVXh3ZzViZUVoYnV5VHRHMVBoczVkZGRCamZmdHlhMnBYQ29Wams5VVVUVXdQN3lLa3hVN3RHVVJXV2RzTjF3WWdSemZnbUNaWWNNdTFwOUg4cmlpZUVTb3Yzbk5uNG1BZ1lXM05iTG02VnJmRSIsImlzc3VhbmNlRGF0ZSI6IjIwMjEtMTEtMDFUMDA6MDA6MDBaIiwidmFsaWRGcm9tIjoiMjAyMS0xMS0wMVQwMDowMDowMFoiLCJpc3N1ZWQiOiIyMDIxLTEwLTMwVDAwOjAwOjAwWiIsImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmtleTp6MmRtekQ4MWNnUHg4VmtpN0pidXVNbUZZcldQZ1lveXR5a1VaM2V5cWh0MWo5S2JyektHWnBWYVhqWW1LbVJadWNFWXVqWTdiMWdZNFM0b2Y2eVpnaDV2ZDM0ZmkxRExHaTZHNU1taEpIYXczaHZYSHVjeGk0czdqRGhCYUU3ekM0RGFtOFpQZjFRNWozaFo0Q0FOTFBjS1FSMWRRRXI4MUxoTVhSNGVFc2IyVjNEN1hTIiwicGVyc29uYWxJZGVudGlmaWVyIjoiSVQvREUvMTIzNCIsImZhbWlseU5hbWUiOiJDYXN0YWZpb3JpIiwiZmlyc3ROYW1lIjoiQmlhbmNhIiwiZGF0ZU9mQmlydGgiOiIxOTMwLTEwLTAxIn0sImNyZWRlbnRpYWxTY2hlbWEiOnsiaWQiOiJodHRwczovL2FwaS10ZXN0LmVic2kuZXUvdHJ1c3RlZC1zY2hlbWFzLXJlZ2lzdHJ5L3YyL3NjaGVtYXMvejNNZ1VGVWtiNzIydXE0eDNkdjV5QUptbk5tekRGZUs1VUM4eDgzUW9lTEpNIiwidHlwZSI6IkZ1bGxKc29uU2NoZW1hVmFsaWRhdG9yMjAyMSJ9LCJleHBpcmF0aW9uRGF0ZSI6IjIwMzEtMTEtMzBUMDA6MDA6MDBaIn19.q6q5uuWdmGUif5XxkUl8NQR4zfGQHrLXLdWVWmfoyPISSLWT4keNXzqjMcOxnU06kBA2zM95x-HjtQneazJRHA

Verifying JWTs

Prerequisites

Verify a VC JWT using the verifyCredentialJwt function:

import { verifyCredentialJwt } from "@cef-ebsi/verifiable-credential";

const vcJwt =
"eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QiLCJraWQiOiJkaWQ6ZWJzaTp6ejdYc0M5aXhBWHVaZWNvRDlzWkVNMSNrTE1Nd012Z19NQUpHNnRQcVVVQVJyTlFocXhVU1BHdkJnMnNtQ3JMX2MwIn0.eyJqdGkiOiJ1cm46ZGlkOjEyMzQ1NiIsInN1YiI6ImRpZDprZXk6ejJkbXpEODFjZ1B4OFZraTdKYnV1TW1GWXJXUGdZb3l0eWtVWjNleXFodDFqOUtickR0NHp4WG9EckJXWUZpQVRZWjhHOUpNZUVYQzdLa2kyNGZiVHd0c0piR2U1cWNia1lGdW5TemNEb2tNUm1qOFVKMVBiZENHaDMzbWY5N0szVG84OWJNemQxNXFyWXEzVmtEenRvWnFmbXVqa0pWcHZUYnFvWFdYcXhtek5EYnZNSiIsImlzcyI6ImRpZDplYnNpOnp6N1hzQzlpeEFYdVplY29EOXNaRU0xIiwibmJmIjoxNjM1NzI0ODAwLCJleHAiOjE5NTM3NjMyMDAsImlhdCI6MTYzNTU1MjAwMCwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiXSwiaWQiOiJ1cm46ZGlkOjEyMzQ1NiIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJWZXJpZmlhYmxlQXR0ZXN0YXRpb24iLCJWZXJpZmlhYmxlSWQiXSwiaXNzdWVyIjoiZGlkOmVic2k6eno3WHNDOWl4QVh1WmVjb0Q5c1pFTTEiLCJpc3N1YW5jZURhdGUiOiIyMDIxLTExLTAxVDAwOjAwOjAwWiIsInZhbGlkRnJvbSI6IjIwMjEtMTEtMDFUMDA6MDA6MDBaIiwiaXNzdWVkIjoiMjAyMS0xMC0zMFQwMDowMDowMFoiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDprZXk6ejJkbXpEODFjZ1B4OFZraTdKYnV1TW1GWXJXUGdZb3l0eWtVWjNleXFodDFqOUtickR0NHp4WG9EckJXWUZpQVRZWjhHOUpNZUVYQzdLa2kyNGZiVHd0c0piR2U1cWNia1lGdW5TemNEb2tNUm1qOFVKMVBiZENHaDMzbWY5N0szVG84OWJNemQxNXFyWXEzVmtEenRvWnFmbXVqa0pWcHZUYnFvWFdYcXhtek5EYnZNSiIsInBlcnNvbmFsSWRlbnRpZmllciI6IklUL0RFLzEyMzQiLCJmYW1pbHlOYW1lIjoiQ2FzdGFmaW9yaSIsImZpcnN0TmFtZSI6IkJpYW5jYSIsImRhdGVPZkJpcnRoIjoiMTkzMC0xMC0wMSJ9LCJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiaHR0cHM6Ly9hcGktdGVzdC5lYnNpLmV1L3RydXN0ZWQtc2NoZW1hcy1yZWdpc3RyeS92Mi9zY2hlbWFzL3ozTWdVRlVrYjcyMnVxNHgzZHY1eUFKbW5ObXpERmVLNVVDOHg4M1FvZUxKTSIsInR5cGUiOiJGdWxsSnNvblNjaGVtYVZhbGlkYXRvcjIwMjEifSwiZXhwaXJhdGlvbkRhdGUiOiIyMDMxLTExLTMwVDAwOjAwOjAwWiIsInRlcm1zT2ZVc2UiOnsiaWQiOiJodHRwczovL2FwaS10ZXN0LmVic2kuZXUvdHJ1c3RlZC1pc3N1ZXJzLXJlZ2lzdHJ5L3Y0L2lzc3VlcnMvZGlkOmVic2k6eno3WHNDOWl4QVh1WmVjb0Q5c1pFTTEvYXR0cmlidXRlcy83MjAxZDk1ZmVmMDVmNzI2NjdmNTQ1NGMyMTkyZGEyYWEzMGQ5ZTA1MmVlZGRlYTc2NTFiNDc3MThkNmYzMWIwIiwidHlwZSI6Iklzc3VhbmNlQ2VydGlmaWNhdGUifX19.RHS1ftcvhn-6UWIMyqXnYKyQIDamVY6xs2ejUDZMxRVucwa7TizvAF0C0XhkFfpay43hWtYdH1wJqQtpExHqjQ";

const options = {
// REQUIRED. EBSI URI Authority ([userinfo "@"] host [":" port])
ebsiAuthority: "api-test.ebsi.eu",
};

const verifiedVc = await verifyCredentialJwt(vcJwt, options);

console.log(verifiedVc);
/*
{
'@context': [ 'https://www.w3.org/2018/credentials/v1' ],
id: 'urn:did:123456',
type: [ 'VerifiableCredential', 'VerifiableAttestation', 'VerifiableId' ],
issuer: 'did:ebsi:zz7XsC9ixAXuZecoD9sZEM1',
issuanceDate: '2021-11-01T00:00:00Z',
validFrom: '2021-11-01T00:00:00Z',
issued: '2021-10-30T00:00:00Z',
credentialSubject: {
id: 'did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9KbrDt4zxXoDrBWYFiATYZ8G9JMeEXC7Kki24fbTwtsJbGe5qcbkYFunSzcDokMRmj8UJ1PbdCGh33mf97K3To89bMzd15qrYq3VkDztoZqfmujkJVpvTbqoXWXqxmzNDbvMJ',
personalIdentifier: 'IT/DE/1234',
familyName: 'Castafiori',
firstName: 'Bianca',
dateOfBirth: '1930-10-01'
},
credentialSchema: {
id: 'https://api-test.ebsi.eu/trusted-schemas-registry/v2/schemas/z3MgUFUkb722uq4x3dv5yAJmnNmzDFeK5UC8x83QoeLJM',
type: 'FullJsonSchemaValidator2021'
},
expirationDate: '2031-11-30T00:00:00Z',
termsOfUse: {
id: 'https://api-test.ebsi.eu/trusted-issuers-registry/v4/issuers/did:ebsi:zz7XsC9ixAXuZecoD9sZEM1/attributes/7201d95fef05f72667f5454c2192da2aa30d9e052eeddea7651b47718d6f31b0',
type: 'IssuanceCertificate'
}
}
*/

Verification process

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

  1. Header
  • The alg property must be ES256, ES256K, Ed25519, or EdDSA.
  • The kid property must contain the DID of the issuer and the fragment identifier allowing to know which verification method to use.
  1. 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.
  1. Signature
  • The signature must be validated against the verification method from the DID document corresponding to the VC JWT kid header.
  1. Extra validations to the VC payload
  • Validation of @context.
  • Validation of type.
  • Validation of dates.
  1. 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.
  1. 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).
  1. Credential schema
  • The VC credential schema(s) must link to valid EBSI credential schemas, registered in the Trusted Schemas Registry.
  • The credential payload is validated against the aforementioned JSON Schemas.

Try it online

The VC & VP validator tool uses the @cef-ebsi/verifiable-credential to verify VC JWTs.