Create and verify EBSI-compliant W3C Verifiable Presentations 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 VC Framework.
Notes:
- this library implements the VC Data Model 1.1 specification.
- this library only supports
2020-12
JSON Schemas.
Installation
Using npm:
npm i --save @cef-ebsi/verifiable-presentation
Using yarn:
yarn add @cef-ebsi/verifiable-presentation
Usage
By default, the library fetches data from the upcoming APIs. Make sure to configure the ebsiEnvConfig
option according to your needs. See more details below.
Creating VP JWTs
Prerequisites
Create an EbsiIssuer
object to sign VP JWTs:
import type { EbsiIssuer } from "@cef-ebsi/verifiable-presentation";
const signer = {
did: "did:ebsi:zxaYaUtb8pvoAtYNWbKcveg",
kid: "did:ebsi:zxaYaUtb8pvoAtYNWbKcveg#CHxYzOqt38Sx6YBfPYhiEdgcwzWk9ty7k0LBa6h70nc",
publicKeyJwk: {
kty: "EC",
crv: "secp256k1",
x: "YsWJUlskYA_wdyKG8z1LYFICL-KNO7fE3WO5-z7dFa4",
y: "uo7ddu68ZY7YBreYNY0GQm-bgz6Xf3zXba0Bk0MgMWI",
},
privateKeyJwk: {
kty: "EC",
crv: "secp256k1",
x: "YsWJUlskYA_wdyKG8z1LYFICL-KNO7fE3WO5-z7dFa4",
y: "uo7ddu68ZY7YBreYNY0GQm-bgz6Xf3zXba0Bk0MgMWI",
d: "<ECC private key>",
},
alg: "ES256K",
} satisfies EbsiIssuer;
In order to create a valid VP JWT, the signer MUST either be a Legal Entity (LE) registered in the DID Registry (EBSI DID method v1), or a Natural Person using the did:key
method (with the codec jwk_jcs-pub
).
Creating a Verifiable Presentation
Specify a payload matching the EbsiVerifiablePresentation
interface. Create a JWT by signing it with the previously configured issuer and a target audience using the createVerifiablePresentationJwt
function:
import {
createVerifiablePresentationJwt,
type EbsiVerifiablePresentation,
type CreateVerifiablePresentationJwtOptions,
} from "@cef-ebsi/verifiable-presentation";
const vpPayload = {
id: "urn:did:123456",
"@context": ["https://www.w3.org/2018/credentials/v1"],
type: ["VerifiablePresentation"],
holder:
"did:key:zBhBLmYmyihtomRdJJNEKzbPj51o4a3GYFeZoRHSABKUwqdjiQPY2cq3LTGRq36RhoZRqix1eq4uA433QJayHdTi8sxm8qdbAbnTyg9dsXCjD8NN7Etcr4f55mRhn9T1d3d6Ec6HgtpcUfemb4ZVKSCDaBrBydsrKAB3TKWNXAkgnz1hseeqf8Y",
verifiableCredential: [
"eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QiLCJraWQiOiJkaWQ6ZWJzaTp6eGFZYVV0Yjhwdm9BdFlOV2JLY3ZlZyNDSHhZek9xdDM4U3g2WUJmUFloaUVkZ2N3eldrOXR5N2swTEJhNmg3MG5jIn0.eyJqdGkiOiJ1cm46dXVpZDowMDNhMWRkOC1hNWQyLTQyZWYtODE4Mi1lOTIxYzBhOWYyY2QiLCJzdWIiOiJkaWQ6a2V5OnpCaEJMbVlteWlodG9tUmRKSk5FS3piUGo1MW80YTNHWUZlWm9SSFNBQktVd3FkamlRUFkyY3EzTFRHUnEzNlJob1pScWl4MWVxNHVBNDMzUUpheUhkVGk4c3htOHFkYkFiblR5Zzlkc1hDakQ4Tk43RXRjcjRmNTVtUmhuOVQxZDNkNkVjNkhndHBjVWZlbWI0WlZLU0NEYUJyQnlkc3JLQUIzVEtXTlhBa2duejFoc2VlcWY4WSIsImlzcyI6ImRpZDplYnNpOnp4YVlhVXRiOHB2b0F0WU5XYktjdmVnIiwibmJmIjoxNjM1NzI0ODAwLCJleHAiOjE5NTM3NjMyMDAsImlhdCI6MTU5MjgzNTEwNCwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiXSwiaWQiOiJ1cm46dXVpZDowMDNhMWRkOC1hNWQyLTQyZWYtODE4Mi1lOTIxYzBhOWYyY2QiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVmVyaWZpYWJsZUF0dGVzdGF0aW9uIl0sImlzc3VlciI6ImRpZDplYnNpOnp4YVlhVXRiOHB2b0F0WU5XYktjdmVnIiwiaXNzdWFuY2VEYXRlIjoiMjAyMS0xMS0wMVQwMDowMDowMFoiLCJ2YWxpZEZyb20iOiIyMDIxLTExLTAxVDAwOjAwOjAwWiIsInZhbGlkVW50aWwiOiIyMDUwLTExLTAxVDAwOjAwOjAwWiIsImV4cGlyYXRpb25EYXRlIjoiMjAzMS0xMS0zMFQwMDowMDowMFoiLCJpc3N1ZWQiOiIyMDIwLTA2LTIyVDE0OjExOjQ0WiIsImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmtleTp6QmhCTG1ZbXlpaHRvbVJkSkpORUt6YlBqNTFvNGEzR1lGZVpvUkhTQUJLVXdxZGppUVBZMmNxM0xUR1JxMzZSaG9aUnFpeDFlcTR1QTQzM1FKYXlIZFRpOHN4bThxZGJBYm5UeWc5ZHNYQ2pEOE5ON0V0Y3I0ZjU1bVJobjlUMWQzZDZFYzZIZ3RwY1VmZW1iNFpWS1NDRGFCckJ5ZHNyS0FCM1RLV05YQWtnbnoxaHNlZXFmOFkifSwiY3JlZGVudGlhbFNjaGVtYSI6eyJpZCI6Imh0dHBzOi8vYXBpLXBpbG90LmVic2kuZXUvdHJ1c3RlZC1zY2hlbWFzLXJlZ2lzdHJ5L3YzL3NjaGVtYXMvejNNZ1VGVWtiNzIydXE0eDNkdjV5QUptbk5tekRGZUs1VUM4eDgzUW9lTEpNIiwidHlwZSI6IkZ1bGxKc29uU2NoZW1hVmFsaWRhdG9yMjAyMSJ9LCJ0ZXJtc09mVXNlIjp7ImlkIjoiaHR0cHM6Ly9hcGktcGlsb3QuZWJzaS5ldS90cnVzdGVkLWlzc3VlcnMtcmVnaXN0cnkvdjUvaXNzdWVycy9kaWQ6ZWJzaTp6eGFZYVV0Yjhwdm9BdFlOV2JLY3ZlZy9hdHRyaWJ1dGVzL2I0MGZkOWI0MDQ0MThhNDRkMmQ5OTExMzc3YTAzMTMwZGRlNDUwZWI1NDZjNzU1YjViODBhY2Q3ODI5MDJlNmQiLCJ0eXBlIjoiSXNzdWFuY2VDZXJ0aWZpY2F0ZSJ9fX0.fKCREswG43_862Vr8L3lJORgFNzvMZ2hR7p93gfEkhM-qhIIlSlP0AcAgy0c6qu2_2uAIC7mOGnj9AZ3Au2nLw",
],
} satisfies EbsiVerifiablePresentation;
const audience = "did:ebsi:zwNAE5xThBpmGJUWAY23kgx";
const vpOptions = {
// REQUIRED. EBSI URI Authority ([userinfo "@"] host [":" port])
ebsiAuthority: "api-pilot.ebsi.eu",
// OPTIONAL. EBSI environment configuration.
// This option allows you to override the default URLs (TIR, DIDR, TSR).
ebsiEnvConfig: {
didRegistry: "https://api-pilot.ebsi.eu/did-registry/v5/identifiers",
trustedIssuersRegistry:
"https://api-pilot.ebsi.eu/trusted-issuers-registry/v5/issuers",
trustedPoliciesRegistry:
"https://api-pilot.ebsi.eu/trusted-policies-registry/v3/users",
},
// OPTIONAL. User-defined VP JWT `nbf`.
// If none is provided, fallback to `payload.verifiableCredential` JWTs highest `nbf`.
nbf: 1686048193,
// OPTIONAL. User-defined VP JWT `exp`.
// If none is provided, fallback to `payload.verifiableCredential` JWTs lowest `exp`.
exp: 1686078193,
// OPTIONAL. The nonce is used to stop a replay attack.
nonce: "nonce",
// OPTIONAL. Verification relationship.
// One of "assertionMethod" | "authentication" | "capabilityDelegation" | "capabilityInvocation"
// Default: "authentication"
proofPurpose: "authentication",
// OPTIONAL. Timeout after which the requests made by the library will fail. Default: 15 seconds
timeout: 15_000,
// OPTIONAL. List of trusted hostnames. To be used if the Core Services APIs are hosted on a custom host for instance.
// If `trustedHostnames` is not defined, the default trusted domains are: "api-test.ebsi.eu", "api-conformance.ebsi.eu", and "api-pilot.ebsi.eu"
trustedHostnames: ["api.example.net"],
// OPTIONAL. Determines whether to validate the Verifiable Presentation payload or not.
// Validation is active by default.
// Note: even when skipValidation is set to true, the payload must be a valid EBSI Verifiable Presentation.
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,
} satisfies CreateVerifiablePresentationJwtOptions;
const vpJwt = await createVerifiablePresentationJwt(
vpPayload,
signer,
audience,
vpOptions,
);
console.log(vpJwt);
// eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QiLCJraWQiOiJkaWQ6a2V5OnpCaEJMbVlteWlodG9tUmRKSk5FS3piUGo1MW80YTNHWUZlWm9SSFNBQktVd3FkamlRUFkyY3EzTFRHUnEzNlJob1pScWl4MWVxNHVBNDMzUUpheUhkVGk4c3htOHFkYkFiblR5Zzlkc1hDakQ4Tk43RXRjcjRmNTVtUmhuOVQxZDNkNkVjNkhndHBjVWZlbWI0WlZLU0NEYUJyQnlkc3JLQUIzVEtXTlhBa2duejFoc2VlcWY4WSN6QmhCTG1ZbXlpaHRvbVJkSkpORUt6YlBqNTFvNGEzR1lGZVpvUkhTQUJLVXdxZGppUVBZMmNxM0xUR1JxMzZSaG9aUnFpeDFlcTR1QTQzM1FKYXlIZFRpOHN4bThxZGJBYm5UeWc5ZHNYQ2pEOE5ON0V0Y3I0ZjU1bVJobjlUMWQzZDZFYzZIZ3RwY1VmZW1iNFpWS1NDRGFCckJ5ZHNyS0FCM1RLV05YQWtnbnoxaHNlZXFmOFkifQ.eyJqdGkiOiJ1cm46ZGlkOjEyMzQ1NiIsInN1YiI6ImRpZDprZXk6ekJoQkxtWW15aWh0b21SZEpKTkVLemJQajUxbzRhM0dZRmVab1JIU0FCS1V3cWRqaVFQWTJjcTNMVEdScTM2UmhvWlJxaXgxZXE0dUE0MzNRSmF5SGRUaThzeG04cWRiQWJuVHlnOWRzWENqRDhOTjdFdGNyNGY1NW1SaG45VDFkM2Q2RWM2SGd0cGNVZmVtYjRaVktTQ0RhQnJCeWRzcktBQjNUS1dOWEFrZ256MWhzZWVxZjhZIiwiaXNzIjoiZGlkOmtleTp6QmhCTG1ZbXlpaHRvbVJkSkpORUt6YlBqNTFvNGEzR1lGZVpvUkhTQUJLVXdxZGppUVBZMmNxM0xUR1JxMzZSaG9aUnFpeDFlcTR1QTQzM1FKYXlIZFRpOHN4bThxZGJBYm5UeWc5ZHNYQ2pEOE5ON0V0Y3I0ZjU1bVJobjlUMWQzZDZFYzZIZ3RwY1VmZW1iNFpWS1NDRGFCckJ5ZHNyS0FCM1RLV05YQWtnbnoxaHNlZXFmOFkiLCJpYXQiOjE3MDE3ODE5MTUsImF1ZCI6ImRpZDplYnNpOnp3TkFFNXhUaEJwbUdKVVdBWTIza2d4IiwidnAiOnsiaWQiOiJ1cm46ZGlkOjEyMzQ1NiIsIkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZVByZXNlbnRhdGlvbiJdLCJob2xkZXIiOiJkaWQ6a2V5OnpCaEJMbVlteWlodG9tUmRKSk5FS3piUGo1MW80YTNHWUZlWm9SSFNBQktVd3FkamlRUFkyY3EzTFRHUnEzNlJob1pScWl4MWVxNHVBNDMzUUpheUhkVGk4c3htOHFkYkFiblR5Zzlkc1hDakQ4Tk43RXRjcjRmNTVtUmhuOVQxZDNkNkVjNkhndHBjVWZlbWI0WlZLU0NEYUJyQnlkc3JLQUIzVEtXTlhBa2duejFoc2VlcWY4WSIsInZlcmlmaWFibGVDcmVkZW50aWFsIjpbImV5SmhiR2NpT2lKRlV6STFOa3NpTENKMGVYQWlPaUpLVjFRaUxDSnJhV1FpT2lKa2FXUTZaV0p6YVRwNmVHRlpZVlYwWWpod2RtOUJkRmxPVjJKTFkzWmxaeU5EU0hoWmVrOXhkRE00VTNnMldVSm1VRmxvYVVWa1oyTjNlbGRyT1hSNU4yc3dURUpoTm1nM01HNWpJbjAuZXlKcWRHa2lPaUoxY200NmRYVnBaRG93TUROaE1XUmtPQzFoTldReUxUUXlaV1l0T0RFNE1pMWxPVEl4WXpCaE9XWXlZMlFpTENKemRXSWlPaUprYVdRNmEyVjVPbnBDYUVKTWJWbHRlV2xvZEc5dFVtUktTazVGUzNwaVVHbzFNVzgwWVROSFdVWmxXbTlTU0ZOQlFrdFZkM0ZrYW1sUlVGa3lZM0V6VEZSSFVuRXpObEpvYjFwU2NXbDRNV1Z4TkhWQk5ETXpVVXBoZVVoa1ZHazRjM2h0T0hGa1lrRmlibFI1Wnpsa2MxaERha1E0VGs0M1JYUmpjalJtTlRWdFVtaHVPVlF4WkROa05rVmpOa2huZEhCalZXWmxiV0kwV2xaTFUwTkVZVUp5UW5sa2MzSkxRVUl6VkV0WFRsaEJhMmR1ZWpGb2MyVmxjV1k0V1NJc0ltbHpjeUk2SW1ScFpEcGxZbk5wT25wNFlWbGhWWFJpT0hCMmIwRjBXVTVYWWt0amRtVm5JaXdpYm1KbUlqb3hOak0xTnpJME9EQXdMQ0psZUhBaU9qRTVOVE0zTmpNeU1EQXNJbWxoZENJNk1UVTVNamd6TlRFd05Dd2lkbU1pT25zaVFHTnZiblJsZUhRaU9sc2lhSFIwY0hNNkx5OTNkM2N1ZHpNdWIzSm5Mekl3TVRndlkzSmxaR1Z1ZEdsaGJITXZkakVpWFN3aWFXUWlPaUoxY200NmRYVnBaRG93TUROaE1XUmtPQzFoTldReUxUUXlaV1l0T0RFNE1pMWxPVEl4WXpCaE9XWXlZMlFpTENKMGVYQmxJanBiSWxabGNtbG1hV0ZpYkdWRGNtVmtaVzUwYVdGc0lpd2lWbVZ5YVdacFlXSnNaVUYwZEdWemRHRjBhVzl1SWwwc0ltbHpjM1ZsY2lJNkltUnBaRHBsWW5OcE9ucDRZVmxoVlhSaU9IQjJiMEYwV1U1WFlrdGpkbVZuSWl3aWFYTnpkV0Z1WTJWRVlYUmxJam9pTWpBeU1TMHhNUzB3TVZRd01Eb3dNRG93TUZvaUxDSjJZV3hwWkVaeWIyMGlPaUl5TURJeExURXhMVEF4VkRBd09qQXdPakF3V2lJc0luWmhiR2xrVlc1MGFXd2lPaUl5TURVd0xURXhMVEF4VkRBd09qQXdPakF3V2lJc0ltVjRjR2x5WVhScGIyNUVZWFJsSWpvaU1qQXpNUzB4TVMwek1GUXdNRG93TURvd01Gb2lMQ0pwYzNOMVpXUWlPaUl5TURJd0xUQTJMVEl5VkRFME9qRXhPalEwV2lJc0ltTnlaV1JsYm5ScFlXeFRkV0pxWldOMElqcDdJbWxrSWpvaVpHbGtPbXRsZVRwNlFtaENURzFaYlhscGFIUnZiVkprU2twT1JVdDZZbEJxTlRGdk5HRXpSMWxHWlZwdlVraFRRVUpMVlhkeFpHcHBVVkJaTW1OeE0weFVSMUp4TXpaU2FHOWFVbkZwZURGbGNUUjFRVFF6TTFGS1lYbElaRlJwT0hONGJUaHhaR0pCWW01VWVXYzVaSE5ZUTJwRU9FNU9OMFYwWTNJMFpqVTFiVkpvYmpsVU1XUXpaRFpGWXpaSVozUndZMVZtWlcxaU5GcFdTMU5EUkdGQ2NrSjVaSE55UzBGQ00xUkxWMDVZUVd0bmJub3hhSE5sWlhGbU9Ga2lmU3dpWTNKbFpHVnVkR2xoYkZOamFHVnRZU0k2ZXlKcFpDSTZJbWgwZEhCek9pOHZZWEJwTFhCcGJHOTBMbVZpYzJrdVpYVXZkSEoxYzNSbFpDMXpZMmhsYldGekxYSmxaMmx6ZEhKNUwzWXpMM05qYUdWdFlYTXZlak5OWjFWR1ZXdGlOekl5ZFhFMGVETmtkalY1UVVwdGJrNXRla1JHWlVzMVZVTTRlRGd6VVc5bFRFcE5JaXdpZEhsd1pTSTZJa1oxYkd4S2MyOXVVMk5vWlcxaFZtRnNhV1JoZEc5eU1qQXlNU0o5TENKMFpYSnRjMDltVlhObElqcDdJbWxrSWpvaWFIUjBjSE02THk5aGNHa3RjR2xzYjNRdVpXSnphUzVsZFM5MGNuVnpkR1ZrTFdsemMzVmxjbk10Y21WbmFYTjBjbmt2ZGpVdmFYTnpkV1Z5Y3k5a2FXUTZaV0p6YVRwNmVHRlpZVlYwWWpod2RtOUJkRmxPVjJKTFkzWmxaeTloZEhSeWFXSjFkR1Z6TDJJME1HWmtPV0kwTURRME1UaGhORFJrTW1RNU9URXhNemMzWVRBek1UTXdaR1JsTkRVd1pXSTFORFpqTnpVMVlqVmlPREJoWTJRM09ESTVNREpsTm1RaUxDSjBlWEJsSWpvaVNYTnpkV0Z1WTJWRFpYSjBhV1pwWTJGMFpTSjlmWDAuZktDUkVzd0c0M184NjJWcjhMM2xKT1JnRk56dk1aMmhSN3A5M2dmRWtoTS1xaElJbFNsUDBBY0FneTBjNnF1Ml8ydUFJQzdtT0duajlBWjNBdTJuTHciXX0sIm5iZiI6MTYzNTcyNDgwMCwiZXhwIjoxOTUzNzYzMjAwfQ.hp99g7ue2m0e2XAJd0Z707UGSpL2kVNGFFaK7CtzUuk0POMPUJBZ6vCKa4AxseQV6dDYSulZMTkOlMNAFdZnxw
Verifying JWTs
Prerequisites
Pass in a VP JWT to verify and the target audience using the verifyPresentationJwt
function:
import {
verifyPresentationJwt,
type VerifyPresentationJwtOptions,
} from "@cef-ebsi/verifiable-presentation";
const vpJwt =
"eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QiLCJraWQiOiJkaWQ6a2V5OnpCaEJMbVlteWlodG9tUmRKSk5FS3piUGo1MW80YTNHWUZlWm9SSFNBQktVd3FkamlRUFkyY3EzTFRHUnEzNlJob1pScWl4MWVxNHVBNDMzUUpheUhkVGk4c3htOHFkYkFiblR5Zzlkc1hDakQ4Tk43RXRjcjRmNTVtUmhuOVQxZDNkNkVjNkhndHBjVWZlbWI0WlZLU0NEYUJyQnlkc3JLQUIzVEtXTlhBa2duejFoc2VlcWY4WSN6QmhCTG1ZbXlpaHRvbVJkSkpORUt6YlBqNTFvNGEzR1lGZVpvUkhTQUJLVXdxZGppUVBZMmNxM0xUR1JxMzZSaG9aUnFpeDFlcTR1QTQzM1FKYXlIZFRpOHN4bThxZGJBYm5UeWc5ZHNYQ2pEOE5ON0V0Y3I0ZjU1bVJobjlUMWQzZDZFYzZIZ3RwY1VmZW1iNFpWS1NDRGFCckJ5ZHNyS0FCM1RLV05YQWtnbnoxaHNlZXFmOFkifQ.eyJqdGkiOiJ1cm46ZGlkOjEyMzQ1NiIsInN1YiI6ImRpZDprZXk6ekJoQkxtWW15aWh0b21SZEpKTkVLemJQajUxbzRhM0dZRmVab1JIU0FCS1V3cWRqaVFQWTJjcTNMVEdScTM2UmhvWlJxaXgxZXE0dUE0MzNRSmF5SGRUaThzeG04cWRiQWJuVHlnOWRzWENqRDhOTjdFdGNyNGY1NW1SaG45VDFkM2Q2RWM2SGd0cGNVZmVtYjRaVktTQ0RhQnJCeWRzcktBQjNUS1dOWEFrZ256MWhzZWVxZjhZIiwiaXNzIjoiZGlkOmtleTp6QmhCTG1ZbXlpaHRvbVJkSkpORUt6YlBqNTFvNGEzR1lGZVpvUkhTQUJLVXdxZGppUVBZMmNxM0xUR1JxMzZSaG9aUnFpeDFlcTR1QTQzM1FKYXlIZFRpOHN4bThxZGJBYm5UeWc5ZHNYQ2pEOE5ON0V0Y3I0ZjU1bVJobjlUMWQzZDZFYzZIZ3RwY1VmZW1iNFpWS1NDRGFCckJ5ZHNyS0FCM1RLV05YQWtnbnoxaHNlZXFmOFkiLCJpYXQiOjE3MDE3ODE5MTUsImF1ZCI6ImRpZDplYnNpOnp3TkFFNXhUaEJwbUdKVVdBWTIza2d4IiwidnAiOnsiaWQiOiJ1cm46ZGlkOjEyMzQ1NiIsIkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZVByZXNlbnRhdGlvbiJdLCJob2xkZXIiOiJkaWQ6a2V5OnpCaEJMbVlteWlodG9tUmRKSk5FS3piUGo1MW80YTNHWUZlWm9SSFNBQktVd3FkamlRUFkyY3EzTFRHUnEzNlJob1pScWl4MWVxNHVBNDMzUUpheUhkVGk4c3htOHFkYkFiblR5Zzlkc1hDakQ4Tk43RXRjcjRmNTVtUmhuOVQxZDNkNkVjNkhndHBjVWZlbWI0WlZLU0NEYUJyQnlkc3JLQUIzVEtXTlhBa2duejFoc2VlcWY4WSIsInZlcmlmaWFibGVDcmVkZW50aWFsIjpbImV5SmhiR2NpT2lKRlV6STFOa3NpTENKMGVYQWlPaUpLVjFRaUxDSnJhV1FpT2lKa2FXUTZaV0p6YVRwNmVHRlpZVlYwWWpod2RtOUJkRmxPVjJKTFkzWmxaeU5EU0hoWmVrOXhkRE00VTNnMldVSm1VRmxvYVVWa1oyTjNlbGRyT1hSNU4yc3dURUpoTm1nM01HNWpJbjAuZXlKcWRHa2lPaUoxY200NmRYVnBaRG93TUROaE1XUmtPQzFoTldReUxUUXlaV1l0T0RFNE1pMWxPVEl4WXpCaE9XWXlZMlFpTENKemRXSWlPaUprYVdRNmEyVjVPbnBDYUVKTWJWbHRlV2xvZEc5dFVtUktTazVGUzNwaVVHbzFNVzgwWVROSFdVWmxXbTlTU0ZOQlFrdFZkM0ZrYW1sUlVGa3lZM0V6VEZSSFVuRXpObEpvYjFwU2NXbDRNV1Z4TkhWQk5ETXpVVXBoZVVoa1ZHazRjM2h0T0hGa1lrRmlibFI1Wnpsa2MxaERha1E0VGs0M1JYUmpjalJtTlRWdFVtaHVPVlF4WkROa05rVmpOa2huZEhCalZXWmxiV0kwV2xaTFUwTkVZVUp5UW5sa2MzSkxRVUl6VkV0WFRsaEJhMmR1ZWpGb2MyVmxjV1k0V1NJc0ltbHpjeUk2SW1ScFpEcGxZbk5wT25wNFlWbGhWWFJpT0hCMmIwRjBXVTVYWWt0amRtVm5JaXdpYm1KbUlqb3hOak0xTnpJME9EQXdMQ0psZUhBaU9qRTVOVE0zTmpNeU1EQXNJbWxoZENJNk1UVTVNamd6TlRFd05Dd2lkbU1pT25zaVFHTnZiblJsZUhRaU9sc2lhSFIwY0hNNkx5OTNkM2N1ZHpNdWIzSm5Mekl3TVRndlkzSmxaR1Z1ZEdsaGJITXZkakVpWFN3aWFXUWlPaUoxY200NmRYVnBaRG93TUROaE1XUmtPQzFoTldReUxUUXlaV1l0T0RFNE1pMWxPVEl4WXpCaE9XWXlZMlFpTENKMGVYQmxJanBiSWxabGNtbG1hV0ZpYkdWRGNtVmtaVzUwYVdGc0lpd2lWbVZ5YVdacFlXSnNaVUYwZEdWemRHRjBhVzl1SWwwc0ltbHpjM1ZsY2lJNkltUnBaRHBsWW5OcE9ucDRZVmxoVlhSaU9IQjJiMEYwV1U1WFlrdGpkbVZuSWl3aWFYTnpkV0Z1WTJWRVlYUmxJam9pTWpBeU1TMHhNUzB3TVZRd01Eb3dNRG93TUZvaUxDSjJZV3hwWkVaeWIyMGlPaUl5TURJeExURXhMVEF4VkRBd09qQXdPakF3V2lJc0luWmhiR2xrVlc1MGFXd2lPaUl5TURVd0xURXhMVEF4VkRBd09qQXdPakF3V2lJc0ltVjRjR2x5WVhScGIyNUVZWFJsSWpvaU1qQXpNUzB4TVMwek1GUXdNRG93TURvd01Gb2lMQ0pwYzNOMVpXUWlPaUl5TURJd0xUQTJMVEl5VkRFME9qRXhPalEwV2lJc0ltTnlaV1JsYm5ScFlXeFRkV0pxWldOMElqcDdJbWxrSWpvaVpHbGtPbXRsZVRwNlFtaENURzFaYlhscGFIUnZiVkprU2twT1JVdDZZbEJxTlRGdk5HRXpSMWxHWlZwdlVraFRRVUpMVlhkeFpHcHBVVkJaTW1OeE0weFVSMUp4TXpaU2FHOWFVbkZwZURGbGNUUjFRVFF6TTFGS1lYbElaRlJwT0hONGJUaHhaR0pCWW01VWVXYzVaSE5ZUTJwRU9FNU9OMFYwWTNJMFpqVTFiVkpvYmpsVU1XUXpaRFpGWXpaSVozUndZMVZtWlcxaU5GcFdTMU5EUkdGQ2NrSjVaSE55UzBGQ00xUkxWMDVZUVd0bmJub3hhSE5sWlhGbU9Ga2lmU3dpWTNKbFpHVnVkR2xoYkZOamFHVnRZU0k2ZXlKcFpDSTZJbWgwZEhCek9pOHZZWEJwTFhCcGJHOTBMbVZpYzJrdVpYVXZkSEoxYzNSbFpDMXpZMmhsYldGekxYSmxaMmx6ZEhKNUwzWXpMM05qYUdWdFlYTXZlak5OWjFWR1ZXdGlOekl5ZFhFMGVETmtkalY1UVVwdGJrNXRla1JHWlVzMVZVTTRlRGd6VVc5bFRFcE5JaXdpZEhsd1pTSTZJa1oxYkd4S2MyOXVVMk5vWlcxaFZtRnNhV1JoZEc5eU1qQXlNU0o5TENKMFpYSnRjMDltVlhObElqcDdJbWxrSWpvaWFIUjBjSE02THk5aGNHa3RjR2xzYjNRdVpXSnphUzVsZFM5MGNuVnpkR1ZrTFdsemMzVmxjbk10Y21WbmFYTjBjbmt2ZGpVdmFYTnpkV1Z5Y3k5a2FXUTZaV0p6YVRwNmVHRlpZVlYwWWpod2RtOUJkRmxPVjJKTFkzWmxaeTloZEhSeWFXSjFkR1Z6TDJJME1HWmtPV0kwTURRME1UaGhORFJrTW1RNU9URXhNemMzWVRBek1UTXdaR1JsTkRVd1pXSTFORFpqTnpVMVlqVmlPREJoWTJRM09ESTVNREpsTm1RaUxDSjBlWEJsSWpvaVNYTnpkV0Z1WTJWRFpYSjBhV1pwWTJGMFpTSjlmWDAuZktDUkVzd0c0M184NjJWcjhMM2xKT1JnRk56dk1aMmhSN3A5M2dmRWtoTS1xaElJbFNsUDBBY0FneTBjNnF1Ml8ydUFJQzdtT0duajlBWjNBdTJuTHciXX0sIm5iZiI6MTYzNTcyNDgwMCwiZXhwIjoxOTUzNzYzMjAwfQ.hp99g7ue2m0e2XAJd0Z707UGSpL2kVNGFFaK7CtzUuk0POMPUJBZ6vCKa4AxseQV6dDYSulZMTkOlMNAFdZnxw";
const audience = "did:ebsi:zwNAE5xThBpmGJUWAY23kgx";
const options = {
// REQUIRED. EBSI URI Authority ([userinfo "@"] host [":" port])
ebsiAuthority: "api-pilot.ebsi.eu",
// OPTIONAL. EBSI environment configuration.
// This option allows you to override the default URLs (TIR, DIDR, TSR).
ebsiEnvConfig: {
didRegistry: "https://api-pilot.ebsi.eu/did-registry/v5/identifiers",
trustedIssuersRegistry:
"https://api-pilot.ebsi.eu/trusted-issuers-registry/v5/issuers",
trustedPoliciesRegistry:
"https://api-pilot.ebsi.eu/trusted-policies-registry/v3/users",
},
// OPTIONAL. Timeout after which the requests made by the library will fail. Default: 15 seconds
timeout: 15_000,
// OPTIONAL. List of trusted hostnames. To be used if the Core Services APIs are hosted on a custom host for instance.
// If `trustedHostnames` is not defined, the default trusted domains are: "api-test.ebsi.eu", "api-conformance.ebsi.eu", and "api-pilot.ebsi.eu"
trustedHostnames: ["api.example.net"],
// OPTIONAL. Unix timestamp. Optional comparison date.
// 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. 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 signature of the VP JWT or not.
// Validation is active by default.
skipSignatureValidation: false,
// OPTIONAL. Determines whether to validate the resolution of the VP holder DID or not.
// Validation is active by default.
skipHolderDidResolutionValidation: false,
// OPTIONAL. Verification relationship.
// One of "assertionMethod" | "authentication" | "capabilityDelegation" | "capabilityInvocation"
// Default: "authentication"
proofPurpose: "authentication",
} satisfies VerifyPresentationJwtOptions;
const verifiedVp = await verifyPresentationJwt(vpJwt, audience, options);
console.log(verifiedVp);
/*
{
id: 'urn:did:123456',
'@context': [ 'https://www.w3.org/2018/credentials/v1' ],
type: [ 'VerifiablePresentation' ],
holder: 'did:key:zBhBLmYmyihtomRdJJNEKzbPj51o4a3GYFeZoRHSABKUwqdjiQPY2cq3LTGRq36RhoZRqix1eq4uA433QJayHdTi8sxm8qdbAbnTyg9dsXCjD8NN7Etcr4f55mRhn9T1d3d6Ec6HgtpcUfemb4ZVKSCDaBrBydsrKAB3TKWNXAkgnz1hseeqf8Y',
verifiableCredential: [
'eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QiLCJraWQiOiJkaWQ6ZWJzaTp6eGFZYVV0Yjhwdm9BdFlOV2JLY3ZlZyNDSHhZek9xdDM4U3g2WUJmUFloaUVkZ2N3eldrOXR5N2swTEJhNmg3MG5jIn0.eyJqdGkiOiJ1cm46dXVpZDowMDNhMWRkOC1hNWQyLTQyZWYtODE4Mi1lOTIxYzBhOWYyY2QiLCJzdWIiOiJkaWQ6a2V5OnpCaEJMbVlteWlodG9tUmRKSk5FS3piUGo1MW80YTNHWUZlWm9SSFNBQktVd3FkamlRUFkyY3EzTFRHUnEzNlJob1pScWl4MWVxNHVBNDMzUUpheUhkVGk4c3htOHFkYkFiblR5Zzlkc1hDakQ4Tk43RXRjcjRmNTVtUmhuOVQxZDNkNkVjNkhndHBjVWZlbWI0WlZLU0NEYUJyQnlkc3JLQUIzVEtXTlhBa2duejFoc2VlcWY4WSIsImlzcyI6ImRpZDplYnNpOnp4YVlhVXRiOHB2b0F0WU5XYktjdmVnIiwibmJmIjoxNjM1NzI0ODAwLCJleHAiOjE5NTM3NjMyMDAsImlhdCI6MTU5MjgzNTEwNCwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiXSwiaWQiOiJ1cm46dXVpZDowMDNhMWRkOC1hNWQyLTQyZWYtODE4Mi1lOTIxYzBhOWYyY2QiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVmVyaWZpYWJsZUF0dGVzdGF0aW9uIl0sImlzc3VlciI6ImRpZDplYnNpOnp4YVlhVXRiOHB2b0F0WU5XYktjdmVnIiwiaXNzdWFuY2VEYXRlIjoiMjAyMS0xMS0wMVQwMDowMDowMFoiLCJ2YWxpZEZyb20iOiIyMDIxLTExLTAxVDAwOjAwOjAwWiIsInZhbGlkVW50aWwiOiIyMDUwLTExLTAxVDAwOjAwOjAwWiIsImV4cGlyYXRpb25EYXRlIjoiMjAzMS0xMS0zMFQwMDowMDowMFoiLCJpc3N1ZWQiOiIyMDIwLTA2LTIyVDE0OjExOjQ0WiIsImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmtleTp6QmhCTG1ZbXlpaHRvbVJkSkpORUt6YlBqNTFvNGEzR1lGZVpvUkhTQUJLVXdxZGppUVBZMmNxM0xUR1JxMzZSaG9aUnFpeDFlcTR1QTQzM1FKYXlIZFRpOHN4bThxZGJBYm5UeWc5ZHNYQ2pEOE5ON0V0Y3I0ZjU1bVJobjlUMWQzZDZFYzZIZ3RwY1VmZW1iNFpWS1NDRGFCckJ5ZHNyS0FCM1RLV05YQWtnbnoxaHNlZXFmOFkifSwiY3JlZGVudGlhbFNjaGVtYSI6eyJpZCI6Imh0dHBzOi8vYXBpLXBpbG90LmVic2kuZXUvdHJ1c3RlZC1zY2hlbWFzLXJlZ2lzdHJ5L3YzL3NjaGVtYXMvejNNZ1VGVWtiNzIydXE0eDNkdjV5QUptbk5tekRGZUs1VUM4eDgzUW9lTEpNIiwidHlwZSI6IkZ1bGxKc29uU2NoZW1hVmFsaWRhdG9yMjAyMSJ9LCJ0ZXJtc09mVXNlIjp7ImlkIjoiaHR0cHM6Ly9hcGktcGlsb3QuZWJzaS5ldS90cnVzdGVkLWlzc3VlcnMtcmVnaXN0cnkvdjUvaXNzdWVycy9kaWQ6ZWJzaTp6eGFZYVV0Yjhwdm9BdFlOV2JLY3ZlZy9hdHRyaWJ1dGVzL2I0MGZkOWI0MDQ0MThhNDRkMmQ5OTExMzc3YTAzMTMwZGRlNDUwZWI1NDZjNzU1YjViODBhY2Q3ODI5MDJlNmQiLCJ0eXBlIjoiSXNzdWFuY2VDZXJ0aWZpY2F0ZSJ9fX0.fKCREswG43_862Vr8L3lJORgFNzvMZ2hR7p93gfEkhM-qhIIlSlP0AcAgy0c6qu2_2uAIC7mOGnj9AZ3Au2nLw'
]
}
*/
Axios config defaults
The library uses the global axios instance to make HTTP requests. If needed, you can specify config defaults. More information on axios configuration can be found here.
For instance, if you want to override the default HTTPS agent with a custom agent using https-proxy-agent, you can do it like so:
import axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";
axios.defaults.httpsAgent = new HttpsProxyAgent("http://168.63.76.32:3128");
Try it online
The VC & VP validator tool uses the @cef-ebsi/verifiable-presentation
to verify VP JWTs.