@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
JavaScript
;
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