UNPKG

@sphereon/ssi-sdk.credential-vcdm

Version:

Plugin for working with W3C Verifiable Credentials DataModel 1 and 2 Credentials & Presentations.

489 lines (483 loc) 18.3 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { CredentialIssuer: () => CredentialIssuer, MessageTypes: () => MessageTypes, VcdmCredentialPlugin: () => VcdmCredentialPlugin, W3cMessageHandler: () => W3cMessageHandler, extractIssuer: () => extractIssuer2, isRevoked: () => isRevoked, pickSigningKey: () => pickSigningKey, preProcessCredentialPayload: () => preProcessCredentialPayload, preProcessPresentation: () => preProcessPresentation, removeDIDParameters: () => removeDIDParameters }); module.exports = __toCommonJS(index_exports); // src/message-handler.ts var import_message_handler = require("@veramo/message-handler"); var import_utils = require("@veramo/utils"); var import_did_jwt_vc = require("did-jwt-vc"); var import_uuid = require("uuid"); var import_debug = __toESM(require("debug"), 1); var debug = (0, import_debug.default)("sphereon:vcdm:message-handler"); var MessageTypes = { /** Represents a Verifiable Credential */ vc: "w3c.vc", /** Represents a Verifiable Presentation */ vp: "w3c.vp" }; var W3cMessageHandler = class extends import_message_handler.AbstractMessageHandler { static { __name(this, "W3cMessageHandler"); } async handle(message, context) { const meta = message.getLastMetaData(); if (meta?.type === "JWT" && message.raw) { const { data } = message; try { (0, import_did_jwt_vc.validateJwtPresentationPayload)(data); debug("JWT is", MessageTypes.vp); const presentation = (0, import_did_jwt_vc.normalizePresentation)(message.raw); const credentials = presentation.verifiableCredential; message.id = (0, import_utils.computeEntryHash)(message.raw); message.type = MessageTypes.vp; message.from = presentation.holder; message.to = presentation.verifier?.[0]; if (presentation.tag) { message.threadId = presentation.tag; } message.createdAt = presentation.issuanceDate; message.presentations = [ presentation ]; message.credentials = credentials; return message; } catch (e) { } try { (0, import_did_jwt_vc.validateJwtCredentialPayload)(data); debug("JWT is", MessageTypes.vc); const credential = (0, import_did_jwt_vc.normalizeCredential)(message.raw); message.id = (0, import_utils.computeEntryHash)(message.raw); message.type = MessageTypes.vc; message.from = credential.issuer.id; message.to = credential.credentialSubject.id; if (credential.tag) { message.threadId = credential.tag; } message.createdAt = credential.issuanceDate; message.credentials = [ credential ]; return message; } catch (e) { } } if (message.type === MessageTypes.vc && message.data) { const credential = message.data; const result = await context.agent.verifyCredential({ credential }); if (result.verified) { message.id = (0, import_utils.computeEntryHash)(message.raw || message.id || (0, import_uuid.v4)()); message.type = MessageTypes.vc; message.from = (0, import_utils.extractIssuer)(credential); message.to = credential.credentialSubject.id; if (credential.tag) { message.threadId = credential.tag; } message.createdAt = credential.issuanceDate; message.credentials = [ credential ]; return message; } else { throw new Error(result.error?.message); } } if (message.type === MessageTypes.vp && message.data) { const presentation = message.data; const result = await context.agent.verifyPresentation({ presentation, // FIXME: HARDCODED CHALLENGE VERIFICATION FOR NOW challenge: "VERAMO", domain: "VERAMO" }); if (result.verified) { message.id = (0, import_utils.computeEntryHash)(message.raw || message.id || (0, import_uuid.v4)()); message.type = MessageTypes.vp; message.from = presentation.holder; if (presentation.tag) { message.threadId = presentation.tag; } message.presentations = [ presentation ]; message.credentials = (0, import_utils.asArray)(presentation.verifiableCredential).map(import_utils.decodeCredentialToObject); return message; } else { throw new Error(result.error?.message); } } return super.handle(message, context); } }; // src/vcdmCredentialPlugin.ts var import_ssi_sdk = require("@sphereon/ssi-sdk.core"); var import_ssi_types2 = require("@sphereon/ssi-types"); var import_core = require("@veramo/core"); var import_debug2 = __toESM(require("debug"), 1); // src/functions.ts var import_utils2 = require("@veramo/utils"); var import_did_jwt = require("did-jwt"); var import_ssi_types = require("@sphereon/ssi-types"); var import_ssi_sdk_ext = require("@sphereon/ssi-sdk-ext.did-utils"); function extractIssuer2(input, options = {}) { if (!(0, import_utils2.isDefined)(input)) { return ""; } else if (typeof input === "string") { try { const { payload } = (0, import_did_jwt.decodeJWT)(input.split(`~`)[0]); const iss = payload.iss ?? ""; return !!options.removeParameters ? removeDIDParameters(iss) : iss; } catch (e) { return ""; } } else { let iss; if (input.issuer) { iss = input.issuer; } else if (input.holder) { iss = input.holder; } else { iss = ""; } if (typeof iss !== "string") iss = iss.id ?? ""; return !!options.removeParameters ? removeDIDParameters(iss) : iss; } } __name(extractIssuer2, "extractIssuer"); function removeDIDParameters(did) { return did.replace(/\?.*$/, ""); } __name(removeDIDParameters, "removeDIDParameters"); async function pickSigningKey({ identifier, kmsKeyRef }, context) { const key = await (0, import_ssi_sdk_ext.getKey)({ identifier, vmRelationship: "assertionMethod", kmsKeyRef }, context); return key; } __name(pickSigningKey, "pickSigningKey"); async function isRevoked(credential, context) { if (!credential.credentialStatus) return false; if (typeof context.agent.checkCredentialStatus === "function") { const status = await context.agent.checkCredentialStatus({ credential }); return status?.revoked == true || status?.verified === false; } throw new Error(`invalid_setup: The credential status can't be verified because there is no ICredentialStatusVerifier plugin installed.`); } __name(isRevoked, "isRevoked"); function preProcessCredentialPayload({ credential, now = /* @__PURE__ */ new Date() }) { const credentialContext = (0, import_ssi_types.addVcdmContextIfNeeded)(credential?.["@context"]); const isVdcm1 = (0, import_ssi_types.isVcdm1Credential)(credential); const isVdcm2 = (0, import_ssi_types.isVcdm2Credential)(credential); const credentialType = (0, import_utils2.processEntryToArray)(credential?.type, "VerifiableCredential"); let issuanceDate = credential?.validFrom ?? credential?.issuanceDate ?? (typeof now === "number" ? new Date(now) : now).toISOString(); let expirationDate = credential?.validUntil ?? credential?.expirationDate; if (issuanceDate instanceof Date) { issuanceDate = issuanceDate.toISOString(); } const credentialPayload = { ...credential, "@context": credentialContext, type: credentialType, ...isVdcm1 && { issuanceDate }, ...isVdcm1 && expirationDate && { expirationDate }, ...isVdcm2 && { validFrom: issuanceDate }, ...isVdcm2 && expirationDate && { validUntil: expirationDate } }; if (isVdcm1) { delete credentialPayload.validFrom; delete credentialPayload.validUntil; } else if (isVdcm2) { delete credentialPayload.issuanceDate; delete credentialPayload.expirationDate; } const issuer = extractIssuer2(credentialPayload, { removeParameters: true }); if (!issuer || typeof issuer === "undefined") { throw new Error("invalid_argument: args.credential.issuer must not be empty"); } return { credential: credentialPayload, issuer, now }; } __name(preProcessCredentialPayload, "preProcessCredentialPayload"); function preProcessPresentation(args) { const { presentation, now = /* @__PURE__ */ new Date() } = args; const credentials = presentation?.verifiableCredential ?? []; const v1Credential = credentials.find((cred) => typeof cred === "object" && cred["@context"].includes(import_ssi_types.VCDM_CREDENTIAL_CONTEXT_V1)) ? import_ssi_types.VCDM_CREDENTIAL_CONTEXT_V1 : void 0; const v2Credential = credentials.find((cred) => typeof cred === "object" && cred["@context"].includes(import_ssi_types.VCDM_CREDENTIAL_CONTEXT_V2)) ? import_ssi_types.VCDM_CREDENTIAL_CONTEXT_V2 : void 0; const presentationContext = (0, import_ssi_types.addVcdmContextIfNeeded)(args?.presentation?.["@context"] ?? [], v2Credential ?? v1Credential ?? import_ssi_types.VCDM_CREDENTIAL_CONTEXT_V2); const presentationType = (0, import_utils2.processEntryToArray)(args?.presentation?.type, "VerifiablePresentation"); let issuanceDate = presentation?.validFrom ?? presentation?.issuanceDate ?? (typeof now === "number" ? new Date(now) : now).toISOString(); if (issuanceDate instanceof Date) { issuanceDate = issuanceDate.toISOString(); } const presentationPayload = { ...presentation, "@context": presentationContext, type: presentationType, ...v1Credential && { issuanceDate }, ...v2Credential && { validFrom: issuanceDate } }; if (!(0, import_utils2.isDefined)(presentationPayload.holder) || !presentationPayload.holder) { throw new Error("invalid_argument: args.presentation.holderDID must not be empty"); } if (presentationPayload.verifiableCredential) { presentationPayload.verifiableCredential = presentationPayload.verifiableCredential.map((cred) => { if (typeof cred !== "string" && cred.proof.jwt) { return cred.proof.jwt; } else { return cred; } }); } return { presentation: presentationPayload, holder: removeDIDParameters(presentationPayload.holder) }; } __name(preProcessPresentation, "preProcessPresentation"); // src/vcdmCredentialPlugin.ts var debug2 = (0, import_debug2.default)("sphereon:ssi-sdk:vcdm"); var VcdmCredentialPlugin = class { static { __name(this, "VcdmCredentialPlugin"); } methods; schema = { components: { schemas: { ...import_core.schema.ICredentialIssuer.components.schemas, ...import_core.schema.ICredentialVerifier.components.schemas }, methods: { ...import_core.schema.ICredentialIssuer.components.methods, ...import_core.schema.ICredentialVerifier.components.methods } } }; issuers; constructor(options) { this.issuers = options.issuers; this.methods = { listUsableProofFormats: this.listUsableProofFormats.bind(this), createVerifiableCredential: this.createVerifiableCredential.bind(this), verifyCredential: this.verifyCredential.bind(this), createVerifiablePresentation: this.createVerifiablePresentation.bind(this), verifyPresentation: this.verifyPresentation.bind(this) }; } async listUsableProofFormats(did, context) { const signingOptions = []; const keys = did.keys; for (const key of keys) { for (const issuer of this.issuers) { if (issuer.matchKeyForType(key)) { signingOptions.push(issuer.getTypeProofFormat()); } } } return signingOptions; } /** {@inheritdoc @veramo/core#ICredentialIssuer.createVerifiableCredential} */ async createVerifiableCredential(args, context) { let { proofFormat /* keyRef, removeOriginalFields, now , ...otherOptions */ } = args; const { credential, issuer, now } = preProcessCredentialPayload(args); try { await context.agent.didManagerGet({ did: issuer }); } catch (e) { throw new Error(`invalid_argument: credential.issuer must be a DID managed by this agent. ${e}`); } try { async function findAndIssueCredential(issuers) { for (const issuer2 of issuers) { if (issuer2.canIssueCredentialType({ proofFormat })) { return await issuer2.createVerifiableCredential({ ...args, credential, now }, context); } } throw new Error(`invalid_setup: No issuer found for the requested proof format: ${proofFormat}, supported: ${issuers.map((i) => i.getTypeProofFormat()).join(",")}`); } __name(findAndIssueCredential, "findAndIssueCredential"); const verifiableCredential = await findAndIssueCredential(this.issuers); return verifiableCredential; } catch (error) { debug2(error); return Promise.reject(error); } } /** {@inheritdoc @veramo/core#ICredentialVerifier.verifyCredential} */ async verifyCredential(args, context) { let { credential, policies /*, ...otherOptions*/ } = args; let verifiedCredential; let verificationResult; async function findAndVerifyCredential(issuers) { for (const issuer of issuers) { if (issuer.canVerifyDocumentType({ document: credential })) { return issuer.verifyCredential(args, context); } } const uniform = import_ssi_types2.CredentialMapper.toUniformCredential(args.credential); return Promise.reject(Error(`invalid_setup: No verifier found for the provided credential credential type: ${JSON.stringify(uniform.type)} proof type ${(0, import_ssi_sdk.asArray)(uniform.proof)?.[0]?.type} supported: ${issuers.map((i) => i.getTypeProofFormat()).join(",")}`)); } __name(findAndVerifyCredential, "findAndVerifyCredential"); verificationResult = await findAndVerifyCredential(this.issuers); verifiedCredential = credential; if (policies?.credentialStatus !== false && await isRevoked(verifiedCredential, context)) { const results = verificationResult.results; const partialSingleResult = Array.isArray(results) ? results[0] : { credential, verified: false, log: [] }; const result = { ...partialSingleResult, credential, verified: false, error: { message: "revoked: The credential was revoked by the issuer", errorCode: "revoked" }, log: [ ...partialSingleResult.log ?? [], { id: "revocation_status", valid: false } ] }; verificationResult = { ...verificationResult, verified: false, error: result.error, results: [ result ] }; } return verificationResult; } /** {@inheritdoc @veramo/core#ICredentialIssuer.createVerifiablePresentation} */ async createVerifiablePresentation(args, context) { const { proofFormat } = args; const { presentation } = preProcessPresentation(args); let verifiablePresentation; async function findAndCreatePresentation(issuers) { for (const issuer of issuers) { if (issuer.canIssueCredentialType({ proofFormat })) { return await issuer.createVerifiablePresentation({ ...args, presentation }, context); } } throw new Error(`invalid_setup: No issuer found for the requested proof format: ${proofFormat}, supported: ${issuers.map((i) => i.getTypeProofFormat()).join(",")}`); } __name(findAndCreatePresentation, "findAndCreatePresentation"); verifiablePresentation = await findAndCreatePresentation(this.issuers); return verifiablePresentation; } /** {@inheritdoc @veramo/core#ICredentialVerifier.verifyPresentation} */ async verifyPresentation(args, context) { let { presentation /*domain, challenge, fetchRemoteContexts, policies, ...otherOptions*/ } = args; async function findAndVerifyPresentation(issuers) { for (const issuer of issuers) { if (issuer.canVerifyDocumentType({ document: presentation })) { return issuer.verifyPresentation(args, context); } } throw new Error("invalid_setup: No verifier found for the provided presentation"); } __name(findAndVerifyPresentation, "findAndVerifyPresentation"); const result = await findAndVerifyPresentation(this.issuers); return result; } }; // src/index.ts var CredentialIssuer = VcdmCredentialPlugin; //# sourceMappingURL=index.cjs.map