@sphereon/ssi-sdk.linked-vp 0.34.1-feature.SSISDK.82.linkedVP.328 → 0.36.1-feature.SSISDK.70.integrate.digidentity.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +146 -105
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -5
- package/dist/index.d.ts +6 -5
- package/dist/index.js +146 -105
- package/dist/index.js.map +1 -1
- package/package.json +15 -15
- package/src/__tests__/shared/linkedVPManagerAgentLogic.ts +16 -6
- package/src/agent/LinkedVPManager.ts +49 -23
- package/src/services/LinkedVPService.ts +143 -100
- package/src/types/ILinkedVPManager.ts +6 -1
package/dist/index.js
CHANGED
|
@@ -199,6 +199,26 @@ var LOGGER_NAMESPACE = "sphereon:linked-vp";
|
|
|
199
199
|
// src/services/LinkedVPService.ts
|
|
200
200
|
var logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE);
|
|
201
201
|
var CLOCK_SKEW = 120;
|
|
202
|
+
async function createLinkedVPPresentation(holderDid, credential, agent) {
|
|
203
|
+
logger.debug(`Creating LinkedVP presentation for ${holderDid} of credential ${credential.id}`);
|
|
204
|
+
const originalCredential = extractOriginalCredential(credential);
|
|
205
|
+
const documentFormat = CredentialMapper.detectDocumentType(originalCredential);
|
|
206
|
+
switch (documentFormat) {
|
|
207
|
+
case DocumentFormat.SD_JWT_VC: {
|
|
208
|
+
return createSdJwtPresentation(originalCredential, agent);
|
|
209
|
+
}
|
|
210
|
+
case DocumentFormat.JSONLD: {
|
|
211
|
+
return createJsonLdPresentation(holderDid, originalCredential, agent);
|
|
212
|
+
}
|
|
213
|
+
case DocumentFormat.MSO_MDOC: {
|
|
214
|
+
return createMdocPresentation(originalCredential);
|
|
215
|
+
}
|
|
216
|
+
default: {
|
|
217
|
+
return createJwtPresentation(holderDid, originalCredential, agent);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
__name(createLinkedVPPresentation, "createLinkedVPPresentation");
|
|
202
222
|
function extractOriginalCredential(credential) {
|
|
203
223
|
if (typeof credential === "string") {
|
|
204
224
|
return credential;
|
|
@@ -216,96 +236,95 @@ function extractOriginalCredential(credential) {
|
|
|
216
236
|
return credential;
|
|
217
237
|
}
|
|
218
238
|
__name(extractOriginalCredential, "extractOriginalCredential");
|
|
219
|
-
async function
|
|
220
|
-
|
|
239
|
+
async function createSdJwtPresentation(originalCredential, agent) {
|
|
240
|
+
const decodedSdJwt = await CredentialMapper.decodeSdJwtVcAsync(typeof originalCredential === "string" ? originalCredential : originalCredential.compactSdJwtVc, defaultGenerateDigest);
|
|
241
|
+
const hashAlg = decodedSdJwt.signedPayload._sd_alg ?? "sha-256";
|
|
242
|
+
const sdHash = calculateSdHash(decodedSdJwt.compactSdJwtVc, hashAlg, defaultGenerateDigest);
|
|
243
|
+
const kbJwtPayload = {
|
|
244
|
+
iat: Math.floor(Date.now() / 1e3 - CLOCK_SKEW),
|
|
245
|
+
sd_hash: sdHash
|
|
246
|
+
};
|
|
247
|
+
const presentationResult = await agent.createSdJwtPresentation({
|
|
248
|
+
presentation: decodedSdJwt.compactSdJwtVc,
|
|
249
|
+
kb: {
|
|
250
|
+
payload: kbJwtPayload
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
return {
|
|
254
|
+
documentFormat: DocumentFormat.SD_JWT_VC,
|
|
255
|
+
presentationPayload: presentationResult.presentation
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
__name(createSdJwtPresentation, "createSdJwtPresentation");
|
|
259
|
+
async function createJsonLdPresentation(holderDid, originalCredential, agent) {
|
|
260
|
+
const vcObject = typeof originalCredential === "string" ? JSON.parse(originalCredential) : originalCredential;
|
|
261
|
+
const vpObject = {
|
|
262
|
+
"@context": [
|
|
263
|
+
"https://www.w3.org/2018/credentials/v1"
|
|
264
|
+
],
|
|
265
|
+
type: [
|
|
266
|
+
"VerifiablePresentation"
|
|
267
|
+
],
|
|
268
|
+
verifiableCredential: [
|
|
269
|
+
vcObject
|
|
270
|
+
],
|
|
271
|
+
holder: holderDid
|
|
272
|
+
};
|
|
221
273
|
const identifier = await agent.identifierManagedGet({
|
|
222
274
|
identifier: holderDid
|
|
223
275
|
});
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
sd_hash: sdHash
|
|
234
|
-
};
|
|
235
|
-
const presentationResult = await agent.createSdJwtPresentation({
|
|
236
|
-
presentation: decodedSdJwt.compactSdJwtVc,
|
|
237
|
-
kb: {
|
|
238
|
-
payload: kbJwtPayload
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
return {
|
|
242
|
-
documentFormat,
|
|
243
|
-
presentationPayload: presentationResult.presentation
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
case DocumentFormat.JSONLD: {
|
|
247
|
-
const vcObject = typeof originalCredential === "string" ? JSON.parse(originalCredential) : originalCredential;
|
|
248
|
-
const vpObject = {
|
|
249
|
-
"@context": [
|
|
250
|
-
"https://www.w3.org/2018/credentials/v1"
|
|
251
|
-
],
|
|
252
|
-
type: [
|
|
253
|
-
"VerifiablePresentation"
|
|
254
|
-
],
|
|
255
|
-
verifiableCredential: [
|
|
256
|
-
vcObject
|
|
257
|
-
],
|
|
258
|
-
holder: holderDid
|
|
259
|
-
};
|
|
260
|
-
const verifiablePresentationSP = await agent.createVerifiablePresentation({
|
|
261
|
-
presentation: vpObject,
|
|
262
|
-
proofFormat: "lds",
|
|
263
|
-
keyRef: identifier.kmsKeyRef || identifier.kid
|
|
264
|
-
});
|
|
265
|
-
return {
|
|
266
|
-
documentFormat,
|
|
267
|
-
presentationPayload: verifiablePresentationSP
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
case DocumentFormat.MSO_MDOC: {
|
|
271
|
-
logger.warning("mso_mdoc format has basic support - production use requires proper mdoc VP token implementation");
|
|
272
|
-
return {
|
|
273
|
-
documentFormat,
|
|
274
|
-
presentationPayload: originalCredential
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
default: {
|
|
278
|
-
const vcJwt = typeof originalCredential === "string" ? originalCredential : JSON.stringify(originalCredential);
|
|
279
|
-
const vpPayload = {
|
|
280
|
-
iss: holderDid,
|
|
281
|
-
vp: {
|
|
282
|
-
"@context": [
|
|
283
|
-
"https://www.w3.org/2018/credentials/v1"
|
|
284
|
-
],
|
|
285
|
-
type: [
|
|
286
|
-
"VerifiablePresentation"
|
|
287
|
-
],
|
|
288
|
-
holder: holderDid,
|
|
289
|
-
verifiableCredential: [
|
|
290
|
-
vcJwt
|
|
291
|
-
]
|
|
292
|
-
},
|
|
293
|
-
iat: Math.floor(Date.now() / 1e3 - CLOCK_SKEW),
|
|
294
|
-
exp: Math.floor(Date.now() / 1e3 + 600 + CLOCK_SKEW)
|
|
295
|
-
};
|
|
296
|
-
const vpJwt = await agent.createVerifiablePresentation({
|
|
297
|
-
presentation: vpPayload.vp,
|
|
298
|
-
proofFormat: "jwt",
|
|
299
|
-
keyRef: identifier.kmsKeyRef || identifier.kid
|
|
300
|
-
});
|
|
301
|
-
return {
|
|
302
|
-
documentFormat,
|
|
303
|
-
presentationPayload: vpJwt.proof && "jwt" in vpJwt.proof && vpJwt.proof.jwt || vpJwt
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
}
|
|
276
|
+
const verifiablePresentationSP = await agent.createVerifiablePresentation({
|
|
277
|
+
presentation: vpObject,
|
|
278
|
+
proofFormat: "lds",
|
|
279
|
+
keyRef: identifier.kmsKeyRef || identifier.kid
|
|
280
|
+
});
|
|
281
|
+
return {
|
|
282
|
+
documentFormat: DocumentFormat.JSONLD,
|
|
283
|
+
presentationPayload: verifiablePresentationSP
|
|
284
|
+
};
|
|
307
285
|
}
|
|
308
|
-
__name(
|
|
286
|
+
__name(createJsonLdPresentation, "createJsonLdPresentation");
|
|
287
|
+
async function createMdocPresentation(originalCredential) {
|
|
288
|
+
logger.warning("mso_mdoc format has basic support - production use requires proper mdoc VP token implementation");
|
|
289
|
+
return {
|
|
290
|
+
documentFormat: DocumentFormat.MSO_MDOC,
|
|
291
|
+
presentationPayload: originalCredential
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
__name(createMdocPresentation, "createMdocPresentation");
|
|
295
|
+
async function createJwtPresentation(holderDid, originalCredential, agent) {
|
|
296
|
+
const vcJwt = typeof originalCredential === "string" ? originalCredential : JSON.stringify(originalCredential);
|
|
297
|
+
const identifier = await agent.identifierManagedGet({
|
|
298
|
+
identifier: holderDid
|
|
299
|
+
});
|
|
300
|
+
const vpPayload = {
|
|
301
|
+
iss: holderDid,
|
|
302
|
+
vp: {
|
|
303
|
+
"@context": [
|
|
304
|
+
"https://www.w3.org/2018/credentials/v1"
|
|
305
|
+
],
|
|
306
|
+
type: [
|
|
307
|
+
"VerifiablePresentation"
|
|
308
|
+
],
|
|
309
|
+
holder: holderDid,
|
|
310
|
+
verifiableCredential: [
|
|
311
|
+
vcJwt
|
|
312
|
+
]
|
|
313
|
+
},
|
|
314
|
+
iat: Math.floor(Date.now() / 1e3 - CLOCK_SKEW),
|
|
315
|
+
exp: Math.floor(Date.now() / 1e3 + 600 + CLOCK_SKEW)
|
|
316
|
+
};
|
|
317
|
+
const vpJwt = await agent.createVerifiablePresentation({
|
|
318
|
+
presentation: vpPayload.vp,
|
|
319
|
+
proofFormat: "jwt",
|
|
320
|
+
keyRef: identifier.kmsKeyRef || identifier.kid
|
|
321
|
+
});
|
|
322
|
+
return {
|
|
323
|
+
documentFormat: DocumentFormat.JWT,
|
|
324
|
+
presentationPayload: vpJwt.proof && "jwt" in vpJwt.proof && vpJwt.proof.jwt || vpJwt
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
__name(createJwtPresentation, "createJwtPresentation");
|
|
309
328
|
|
|
310
329
|
// src/agent/LinkedVPManager.ts
|
|
311
330
|
var linkedVPManagerMethods = [
|
|
@@ -327,10 +346,6 @@ var LinkedVPManager = class {
|
|
|
327
346
|
lvpGetServiceEntries: this.lvpGetServiceEntries.bind(this),
|
|
328
347
|
lvpGeneratePresentation: this.lvpGeneratePresentation.bind(this)
|
|
329
348
|
};
|
|
330
|
-
holderDids;
|
|
331
|
-
constructor(options) {
|
|
332
|
-
this.holderDids = options.holderDids;
|
|
333
|
-
}
|
|
334
349
|
async lvpPublishCredential(args, context) {
|
|
335
350
|
const { digitalCredentialId } = args;
|
|
336
351
|
const credential = await context.agent.crsGetCredential({
|
|
@@ -341,17 +356,19 @@ var LinkedVPManager = class {
|
|
|
341
356
|
}
|
|
342
357
|
const linkedVpId = this.buildLinkedVpId(args.linkedVpId, credential.tenantId);
|
|
343
358
|
await this.ensureLinkedVpIdUnique(linkedVpId, context, credential.tenantId);
|
|
344
|
-
const
|
|
359
|
+
const publishAt = args.linkedVpFrom ?? /* @__PURE__ */ new Date();
|
|
345
360
|
await context.agent.crsUpdateCredential({
|
|
346
361
|
id: digitalCredentialId,
|
|
347
362
|
linkedVpId,
|
|
348
|
-
linkedVpFrom:
|
|
363
|
+
linkedVpFrom: publishAt,
|
|
364
|
+
linkedVpUntil: args.linkedVpUntil
|
|
349
365
|
});
|
|
350
366
|
return {
|
|
351
367
|
id: credential.id,
|
|
352
368
|
linkedVpId,
|
|
353
369
|
tenantId: credential.tenantId,
|
|
354
|
-
linkedVpFrom:
|
|
370
|
+
linkedVpFrom: publishAt,
|
|
371
|
+
linkedVpUntil: args.linkedVpUntil,
|
|
355
372
|
createdAt: credential.createdAt
|
|
356
373
|
};
|
|
357
374
|
}
|
|
@@ -391,31 +408,37 @@ var LinkedVPManager = class {
|
|
|
391
408
|
}
|
|
392
409
|
}
|
|
393
410
|
async lvpGetServiceEntries(args, context) {
|
|
394
|
-
const { tenantId } = args;
|
|
411
|
+
const { tenantId, subjectDid } = args;
|
|
395
412
|
const filter = {
|
|
396
413
|
linkedVpId: Not(IsNull())
|
|
397
414
|
};
|
|
398
415
|
if (tenantId) {
|
|
399
416
|
filter.tenantId = tenantId;
|
|
400
417
|
}
|
|
418
|
+
if (subjectDid) {
|
|
419
|
+
filter.subjectCorrelationId = subjectDid;
|
|
420
|
+
}
|
|
401
421
|
const credentials = await context.agent.crsGetCredentials({
|
|
402
422
|
filter: [
|
|
403
423
|
filter
|
|
404
424
|
]
|
|
405
425
|
});
|
|
406
|
-
return credentials.
|
|
407
|
-
const
|
|
408
|
-
|
|
426
|
+
return credentials.flatMap((cred) => {
|
|
427
|
+
const uniformDocument = JSON.parse(cred.uniformDocument);
|
|
428
|
+
const holderDidForEntry = this.getHolderDid(uniformDocument);
|
|
429
|
+
return holderDidForEntry && holderDidForEntry.startsWith("did:web") ? [
|
|
430
|
+
this.credentialToServiceEntry(cred, holderDidForEntry)
|
|
431
|
+
] : [];
|
|
409
432
|
});
|
|
410
433
|
}
|
|
411
434
|
async lvpGeneratePresentation(args, context) {
|
|
412
435
|
const { linkedVpId } = args;
|
|
413
436
|
const tenantId = this.parseTenantFromLinkedVpId(linkedVpId);
|
|
414
|
-
const holderDid = this.getHolderDid(tenantId);
|
|
415
437
|
const uniqueCredentials = await context.agent.crsGetUniqueCredentials({
|
|
416
438
|
filter: [
|
|
417
439
|
{
|
|
418
440
|
linkedVpId: args.linkedVpId,
|
|
441
|
+
linkedVpUntil: args.linkedVpUntil,
|
|
419
442
|
...tenantId && {
|
|
420
443
|
tenantId
|
|
421
444
|
}
|
|
@@ -428,14 +451,29 @@ var LinkedVPManager = class {
|
|
|
428
451
|
if (uniqueCredentials.length > 1) {
|
|
429
452
|
return Promise.reject(Error(`Multiple credentials found for linkedVpId ${linkedVpId}`));
|
|
430
453
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
454
|
+
const uniqueDigitalCredential = uniqueCredentials[0];
|
|
455
|
+
if (!uniqueDigitalCredential.uniformVerifiableCredential) {
|
|
456
|
+
return Promise.reject(Error(`uniformVerifiableCredential could not be found for credential ${uniqueDigitalCredential.digitalCredential.id}`));
|
|
457
|
+
}
|
|
458
|
+
const holderDid = this.getHolderDid(uniqueDigitalCredential.uniformVerifiableCredential);
|
|
435
459
|
if (!holderDid) {
|
|
436
|
-
|
|
460
|
+
return Promise.reject(Error(`Could not extract the holder did:web from cnf nor the credentialSubject id`));
|
|
461
|
+
}
|
|
462
|
+
return createLinkedVPPresentation(holderDid, uniqueDigitalCredential, context.agent);
|
|
463
|
+
}
|
|
464
|
+
getHolderDid(uniformDocument) {
|
|
465
|
+
if ("cnf" in uniformDocument && "jwk" in uniformDocument.cnf && "kid" in uniformDocument.cnf.jwk) {
|
|
466
|
+
return uniformDocument.cnf.jwk.kid.split("#")[0];
|
|
437
467
|
}
|
|
438
|
-
|
|
468
|
+
if ("credentialSubject" in uniformDocument) {
|
|
469
|
+
const credentialSubject = Array.isArray(uniformDocument.credentialSubject) ? uniformDocument.credentialSubject[0] : uniformDocument.credentialSubject;
|
|
470
|
+
if ("id" in credentialSubject && credentialSubject.id) {
|
|
471
|
+
if (credentialSubject.id.startsWith("did:web")) {
|
|
472
|
+
return credentialSubject.id;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return void 0;
|
|
439
477
|
}
|
|
440
478
|
parseTenantFromLinkedVpId(linkedVpId) {
|
|
441
479
|
const idx = linkedVpId.lastIndexOf("@");
|
|
@@ -461,7 +499,10 @@ var LinkedVPManager = class {
|
|
|
461
499
|
}
|
|
462
500
|
buildLinkedVpId(linkedVpId, tenantId) {
|
|
463
501
|
let finalLinkedVpId = linkedVpId || this.generateLinkedVpId();
|
|
464
|
-
if (
|
|
502
|
+
if (linkedVpId && linkedVpId.includes("@")) {
|
|
503
|
+
throw new Error(`LinkedVP ID cannot contain '@' character as it is reserved for tenant separation`);
|
|
504
|
+
}
|
|
505
|
+
if (tenantId && tenantId !== "") {
|
|
465
506
|
finalLinkedVpId = `${finalLinkedVpId}@${tenantId}`;
|
|
466
507
|
}
|
|
467
508
|
return finalLinkedVpId;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../plugin.schema.json","../src/agent/LinkedVPManager.ts","../src/services/LinkedVPService.ts","../src/types/ILinkedVPManager.ts"],"sourcesContent":["{\n \"ILinkedVPManager\": {\n \"components\": {\n \"schemas\": {\n \"GeneratePresentationArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"linkedVpId\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"linkedVpId\"\n ],\n \"additionalProperties\": false\n },\n \"LinkedVPPresentation\": {\n \"anyOf\": [\n {\n \"type\": \"string\"\n },\n {\n \"$ref\": \"#/components/schemas/Record<string,any>\"\n }\n ]\n },\n \"Record<string,any>\": {\n \"type\": \"object\"\n },\n \"GetServiceEntriesArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"tenantId\": {\n \"type\": \"string\"\n }\n },\n \"additionalProperties\": false\n },\n \"LinkedVPServiceEntry\": {\n \"type\": \"object\",\n \"properties\": {\n \"id\": {\n \"type\": \"string\"\n },\n \"type\": {\n \"type\": \"string\",\n \"const\": \"LinkedVerifiablePresentation\"\n },\n \"serviceEndpoint\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"id\",\n \"type\",\n \"serviceEndpoint\"\n ],\n \"additionalProperties\": false\n },\n \"HasLinkedVPEntryArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"linkedVpId\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"linkedVpId\"\n ],\n \"additionalProperties\": false\n },\n \"PublishCredentialArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"digitalCredentialId\": {\n \"type\": \"string\"\n },\n \"linkedVpId\": {\n \"type\": \"string\"\n },\n \"tenantId\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"digitalCredentialId\"\n ],\n \"additionalProperties\": false\n },\n \"LinkedVPEntry\": {\n \"type\": \"object\",\n \"properties\": {\n \"id\": {\n \"type\": \"string\"\n },\n \"linkedVpId\": {\n \"type\": \"string\"\n },\n \"tenantId\": {\n \"type\": \"string\"\n },\n \"linkedVpFrom\": {\n \"type\": \"string\",\n \"format\": \"date-time\"\n },\n \"createdAt\": {\n \"type\": \"string\",\n \"format\": \"date-time\"\n }\n },\n \"required\": [\n \"id\",\n \"linkedVpId\",\n \"createdAt\"\n ],\n \"additionalProperties\": false\n },\n \"UnpublishCredentialArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"linkedVpId\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"linkedVpId\"\n ],\n \"additionalProperties\": false\n }\n },\n \"methods\": {\n \"lvpGeneratePresentation\": {\n \"description\": \"Generate and return a Verifiable Presentation for a published LinkedVP This is the main endpoint handler for GET /linked-vp/\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/GeneratePresentationArgs\"\n },\n \"returnType\": {\n \"$ref\": \"#/components/schemas/LinkedVPPresentation\"\n }\n },\n \"lvpGetServiceEntries\": {\n \"description\": \"Get LinkedVP service entries for a DID to be added to a DID Document This is useful when generating DID Documents with toDidDocument\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/GetServiceEntriesArgs\"\n },\n \"returnType\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/components/schemas/LinkedVPServiceEntry\"\n }\n }\n },\n \"lvpHasEntry\": {\n \"description\": \"Check if a LinkedVP entry exists by linkedVpId\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/HasLinkedVPEntryArgs\"\n },\n \"returnType\": {\n \"type\": \"boolean\"\n }\n },\n \"lvpPublishCredential\": {\n \"description\": \"Publish a credential as a LinkedVP by adding it to the holder's DID Document\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/PublishCredentialArgs\"\n },\n \"returnType\": {\n \"$ref\": \"#/components/schemas/LinkedVPEntry\"\n }\n },\n \"lvpUnpublishCredential\": {\n \"description\": \"Unpublish a credential by removing its LinkedVP entry from the DID Document\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/UnpublishCredentialArgs\"\n },\n \"returnType\": {\n \"type\": \"boolean\"\n }\n }\n }\n }\n }\n}","import { DigitalCredential } from '@sphereon/ssi-sdk.data-store-types'\nimport { IAgentPlugin } from '@veramo/core'\nimport { IsNull, Not } from 'typeorm'\nimport { schema } from '../index'\nimport { createLinkedVPPresentation } from '../services/LinkedVPService'\nimport {\n GeneratePresentationArgs,\n GetServiceEntriesArgs,\n HasLinkedVPEntryArgs,\n ILinkedVPManager,\n LinkedVPEntry,\n LinkedVPPresentation,\n LinkedVPServiceEntry,\n PublishCredentialArgs,\n RequiredContext,\n UnpublishCredentialArgs,\n} from '../types'\n\n// Exposing the methods here for any REST implementation\nexport const linkedVPManagerMethods: Array<string> = [\n 'lvpPublishCredential',\n 'lvpUnpublishCredential',\n 'lvpHasEntry',\n 'lvpGetServiceEntries',\n 'lvpGeneratePresentation',\n]\n\n/**\n * {@inheritDoc ILinkedVPManager}\n */\nexport class LinkedVPManager implements IAgentPlugin {\n readonly schema = schema.ILinkedVPManager\n readonly methods: ILinkedVPManager = {\n lvpPublishCredential: this.lvpPublishCredential.bind(this),\n lvpUnpublishCredential: this.lvpUnpublishCredential.bind(this),\n lvpHasEntry: this.lvpHasEntry.bind(this),\n lvpGetServiceEntries: this.lvpGetServiceEntries.bind(this),\n lvpGeneratePresentation: this.lvpGeneratePresentation.bind(this),\n }\n\n private readonly holderDids: Record<string, string>\n\n constructor(options: { holderDids: Record<string, string> }) {\n this.holderDids = options.holderDids\n }\n\n private async lvpPublishCredential(args: PublishCredentialArgs, context: RequiredContext): Promise<LinkedVPEntry> {\n const { digitalCredentialId } = args\n\n const credential: DigitalCredential = await context.agent.crsGetCredential({ id: digitalCredentialId })\n\n if (credential.linkedVpId) {\n return Promise.reject(new Error(`Credential ${digitalCredentialId} is already published with linkedVpId ${credential.linkedVpId}`))\n }\n\n const linkedVpId = this.buildLinkedVpId(args.linkedVpId, credential.tenantId)\n\n await this.ensureLinkedVpIdUnique(linkedVpId, context, credential.tenantId)\n\n const publishedAt = new Date()\n await context.agent.crsUpdateCredential({\n id: digitalCredentialId,\n linkedVpId,\n linkedVpFrom: publishedAt,\n })\n\n return {\n id: credential.id,\n linkedVpId,\n tenantId: credential.tenantId,\n linkedVpFrom: publishedAt,\n createdAt: credential.createdAt,\n }\n }\n\n private async lvpUnpublishCredential(args: UnpublishCredentialArgs, context: RequiredContext): Promise<boolean> {\n const { linkedVpId } = args\n\n // Find credential by linkedVpId and tenantId\n const credentials = await context.agent.crsGetCredentials({\n filter: [{ linkedVpId }],\n })\n if (credentials.length === 0) {\n return Promise.reject(Error(`No credential found with linkedVpId ${linkedVpId}`))\n }\n\n const credential = credentials[0]\n await context.agent.crsUpdateCredential({\n id: credential.id,\n linkedVpId: undefined,\n linkedVpFrom: undefined,\n })\n\n return true\n }\n\n private async lvpHasEntry(args: HasLinkedVPEntryArgs, context: RequiredContext): Promise<boolean> {\n const { linkedVpId } = args\n\n try {\n const credentials = await context.agent.crsGetCredentials({\n filter: [{ linkedVpId }],\n })\n return credentials.length > 0\n } catch (error) {\n return false\n }\n }\n\n private async lvpGetServiceEntries(args: GetServiceEntriesArgs, context: RequiredContext): Promise<Array<LinkedVPServiceEntry>> {\n const { tenantId } = args\n\n // Get all published credentials (credentials with linkedVpId set)\n const filter: any = { linkedVpId: Not(IsNull()) }\n if (tenantId) {\n filter.tenantId = tenantId\n }\n\n const credentials = await context.agent.crsGetCredentials({\n filter: [filter],\n })\n\n return credentials\n .filter((cred) => cred.linkedVpId !== undefined && cred.linkedVpId !== null)\n .map((cred) => {\n const holderDidForEntry = this.getHolderDid(cred.tenantId)\n return this.credentialToServiceEntry(cred, holderDidForEntry)\n })\n }\n\n private async lvpGeneratePresentation(args: GeneratePresentationArgs, context: RequiredContext): Promise<LinkedVPPresentation> {\n const { linkedVpId } = args\n const tenantId = this.parseTenantFromLinkedVpId(linkedVpId)\n const holderDid = this.getHolderDid(tenantId)\n\n const uniqueCredentials = await context.agent.crsGetUniqueCredentials({\n filter: [\n {\n linkedVpId: args.linkedVpId,\n ...(tenantId && { tenantId }),\n },\n ],\n })\n if (uniqueCredentials.length === 0) {\n return Promise.reject(Error(`No published credentials found for linkedVpId ${linkedVpId}`))\n }\n if (uniqueCredentials.length > 1) {\n return Promise.reject(Error(`Multiple credentials found for linkedVpId ${linkedVpId}`))\n }\n\n // Generate the Verifiable Presentation with all published credentials\n return createLinkedVPPresentation(holderDid, uniqueCredentials[0], context.agent)\n }\n\n private getHolderDid(tenantId: string | undefined) {\n const holderDid = this.holderDids[tenantId ?? 'default']\n if (!holderDid) {\n throw Error(`No holder did supplied for tenant ${tenantId ?? 'default'}`)\n }\n return holderDid\n }\n\n private parseTenantFromLinkedVpId(linkedVpId: string): string | undefined {\n const idx = linkedVpId.lastIndexOf('@')\n return idx === -1 ? undefined : linkedVpId.substring(idx + 1)\n }\n\n private generateLinkedVpId(): string {\n return `lvp-${Math.random().toString(36).substring(2, 15)}`\n }\n\n private async ensureLinkedVpIdUnique(linkedVpId: string, context: RequiredContext, tenantId?: string): Promise<void> {\n const credentials = await context.agent.crsGetCredentials({\n filter: [{ linkedVpId, ...(tenantId && { tenantId }) }],\n })\n\n if (credentials.length > 0) {\n throw new Error(`LinkedVP ID ${linkedVpId} already exists${tenantId ? ` for tenant ${tenantId}` : ''}`)\n }\n }\n\n private buildLinkedVpId(linkedVpId: string | undefined, tenantId: string | undefined) {\n let finalLinkedVpId = linkedVpId || this.generateLinkedVpId()\n\n // Append tenantId if provided and not already present\n if (tenantId && tenantId !== '' && !finalLinkedVpId.includes('@')) {\n finalLinkedVpId = `${finalLinkedVpId}@${tenantId}`\n }\n return finalLinkedVpId\n }\n\n private getBaseUrlFromDid(holderDid: string): string {\n if (!holderDid.startsWith('did:web:')) {\n throw new Error(`Invalid DID: ${holderDid}, must be did:web`)\n }\n\n const withoutPrefix = holderDid.replace('did:web:', '') // example.com:tenants:tenant1\n const parts = withoutPrefix.split(':')\n const domain = parts.shift()! // example.com\n const path = parts.join('/') // tenants/tenant1\n\n return path\n ? `https://${domain}/${path}` // https://example.com/tenants/tenant1\n : `https://${domain}` // https://example.com\n }\n\n private buildServiceEndpoint(holderDid: string, linkedVpId: string): string {\n const baseUrl = this.getBaseUrlFromDid(holderDid)\n const cleanBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl\n return `${cleanBaseUrl}/linked-vp/${linkedVpId}`\n }\n\n private credentialToServiceEntry(credential: DigitalCredential, holderDid: string): LinkedVPServiceEntry {\n if (!credential.linkedVpId) {\n throw new Error(`Credential ${credential.id} does not have a linkedVpId`)\n }\n\n return {\n id: `${holderDid}#${credential.linkedVpId}`,\n type: 'LinkedVerifiablePresentation',\n serviceEndpoint: this.buildServiceEndpoint(holderDid, credential.linkedVpId),\n }\n }\n}\n","import { UniqueDigitalCredential } from '@sphereon/ssi-sdk.credential-store'\nimport { calculateSdHash, defaultGenerateDigest, PartialSdJwtKbJwt } from '@sphereon/ssi-sdk.sd-jwt'\n\nimport {\n CredentialMapper,\n DocumentFormat,\n Loggers,\n OriginalVerifiableCredential,\n SdJwtDecodedVerifiableCredential,\n WrappedVerifiableCredential,\n} from '@sphereon/ssi-types'\nimport { LinkedVPPresentation, LOGGER_NAMESPACE, RequiredContext } from '../types'\n\nconst logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE)\nconst CLOCK_SKEW = 120 // TODO make adjustable?\n\n/**\n * Extracts the original credential from various wrapper types\n */\nfunction extractOriginalCredential(\n credential: UniqueDigitalCredential | WrappedVerifiableCredential | OriginalVerifiableCredential,\n): OriginalVerifiableCredential {\n if (typeof credential === 'string') {\n return credential\n }\n\n if ('digitalCredential' in credential) {\n const udc = credential as UniqueDigitalCredential\n if (udc.originalVerifiableCredential) {\n return udc.originalVerifiableCredential\n }\n return udc.uniformVerifiableCredential as OriginalVerifiableCredential\n }\n\n if ('original' in credential) {\n return credential.original\n }\n\n return credential as OriginalVerifiableCredential\n}\n\n/**\n * Creates a Verifiable Presentation for LinkedVP publishing\n * Contains multiple credentials in a single JWT VP\n * No nonce or audience since this is for publishing, not responding to verification\n */\nexport async function createLinkedVPPresentation(\n holderDid: string,\n credential: UniqueDigitalCredential,\n agent: RequiredContext['agent'],\n): Promise<LinkedVPPresentation> {\n logger.debug(`Creating LinkedVP presentation for ${holderDid} of credential ${credential.id}`)\n\n const identifier = await agent.identifierManagedGet({ identifier: holderDid })\n const originalCredential = extractOriginalCredential(credential)\n const documentFormat = CredentialMapper.detectDocumentType(originalCredential)\n switch (documentFormat) {\n case DocumentFormat.SD_JWT_VC: {\n // SD-JWT with KB-JWT\n const decodedSdJwt = await CredentialMapper.decodeSdJwtVcAsync(\n typeof originalCredential === 'string' ? originalCredential : (originalCredential as SdJwtDecodedVerifiableCredential).compactSdJwtVc,\n defaultGenerateDigest,\n )\n\n const hashAlg = decodedSdJwt.signedPayload._sd_alg ?? 'sha-256'\n const sdHash = calculateSdHash(decodedSdJwt.compactSdJwtVc, hashAlg, defaultGenerateDigest)\n const kbJwtPayload: PartialSdJwtKbJwt['payload'] = {\n iat: Math.floor(Date.now() / 1000 - CLOCK_SKEW),\n sd_hash: sdHash,\n }\n\n const presentationResult = await agent.createSdJwtPresentation({\n presentation: decodedSdJwt.compactSdJwtVc,\n kb: {\n payload: kbJwtPayload as any, // FIXME?\n },\n })\n\n return {\n documentFormat,\n presentationPayload: presentationResult.presentation,\n }\n }\n case DocumentFormat.JSONLD: {\n // JSON-LD VC - create JSON-LD VP with challenge and domain in proof\n const vcObject = typeof originalCredential === 'string' ? JSON.parse(originalCredential) : originalCredential\n\n const vpObject = {\n '@context': ['https://www.w3.org/2018/credentials/v1'],\n type: ['VerifiablePresentation'],\n verifiableCredential: [vcObject],\n holder: holderDid,\n }\n\n // Create JSON-LD VP with proof\n const verifiablePresentationSP = await agent.createVerifiablePresentation({\n presentation: vpObject,\n proofFormat: 'lds',\n keyRef: identifier.kmsKeyRef || identifier.kid,\n })\n return {\n documentFormat,\n presentationPayload: verifiablePresentationSP,\n }\n }\n case DocumentFormat.MSO_MDOC: {\n // ISO mdoc - create mdoc VP token\n // This is a placeholder implementation\n // Full implementation would require:\n // 1. Decode the mdoc using CredentialMapper or mdoc utilities\n // 2. Build proper mdoc VP token with session transcript\n // 3. Include nonce/audience in the session transcript\n logger.warning('mso_mdoc format has basic support - production use requires proper mdoc VP token implementation')\n\n return {\n documentFormat,\n presentationPayload: originalCredential,\n }\n }\n default: {\n // JWT VC - create JWT VP with nonce and aud in payload\n const vcJwt = typeof originalCredential === 'string' ? originalCredential : JSON.stringify(originalCredential)\n\n // Create VP JWT using agent method\n const vpPayload = {\n iss: holderDid,\n vp: {\n '@context': ['https://www.w3.org/2018/credentials/v1'],\n type: ['VerifiablePresentation'],\n holder: holderDid,\n verifiableCredential: [vcJwt],\n },\n iat: Math.floor(Date.now() / 1000 - CLOCK_SKEW),\n exp: Math.floor(Date.now() / 1000 + 600 + CLOCK_SKEW), // 10 minutes\n }\n\n // Use the agent's JWT creation capability\n const vpJwt = await agent.createVerifiablePresentation({\n presentation: vpPayload.vp,\n proofFormat: 'jwt',\n keyRef: identifier.kmsKeyRef || identifier.kid,\n })\n\n return {\n documentFormat,\n presentationPayload: (vpJwt.proof && 'jwt' in vpJwt.proof && vpJwt.proof.jwt) || vpJwt,\n }\n }\n }\n}\n","import { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'\nimport { ICredentialStore } from '@sphereon/ssi-sdk.credential-store'\nimport { VcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'\nimport { ISDJwtPlugin } from '@sphereon/ssi-sdk.sd-jwt'\nimport { DocumentFormat } from '@sphereon/ssi-types'\nimport { IAgentContext, IPluginMethodMap } from '@veramo/core'\nimport { IKeyManager } from '@veramo/core/src/types/IKeyManager'\n\nexport const LOGGER_NAMESPACE = 'sphereon:linked-vp'\n\nexport type LinkedVPPresentation = {\n documentFormat: DocumentFormat\n presentationPayload: string | Record<string, any>\n}\n\nexport interface ILinkedVPManager extends IPluginMethodMap {\n /**\n * Publish a credential as a LinkedVP by adding it to the holder's DID Document\n * @param args - Publication arguments including credential ID and scope configuration\n * @param context - Agent context\n */\n lvpPublishCredential(args: PublishCredentialArgs, context: RequiredContext): Promise<LinkedVPEntry>\n\n /**\n * Unpublish a credential by removing its LinkedVP entry from the DID Document\n * @param args - Unpublish arguments\n * @param context - Agent context\n */\n lvpUnpublishCredential(args: UnpublishCredentialArgs, context: RequiredContext): Promise<boolean>\n\n /**\n * Check if a LinkedVP entry exists by linkedVpId\n * @param args - Query arguments\n * @param context - Agent context\n */\n lvpHasEntry(args: HasLinkedVPEntryArgs, context: RequiredContext): Promise<boolean>\n\n /**\n * Get LinkedVP service entries for a DID to be added to a DID Document\n * This is useful when generating DID Documents with toDidDocument\n * @param args - Query arguments for the DID\n * @param context - Agent context\n */\n lvpGetServiceEntries(args: GetServiceEntriesArgs, context: RequiredContext): Promise<Array<LinkedVPServiceEntry>>\n\n /**\n * Generate and return a Verifiable Presentation for a published LinkedVP\n * This is the main endpoint handler for GET /linked-vp/{linkedVpId}\n * @param args - Generation arguments\n * @param context - Agent context\n */\n lvpGeneratePresentation(args: GeneratePresentationArgs, context: RequiredContext): Promise<LinkedVPPresentation>\n}\n\nexport type PublishCredentialArgs = {\n digitalCredentialId: string\n linkedVpId?: string // Optional: if not provided, will be auto-generated\n}\n\nexport type UnpublishCredentialArgs = {\n linkedVpId: string\n}\n\nexport type HasLinkedVPEntryArgs = {\n linkedVpId: string\n}\n\nexport type GetServiceEntriesArgs = {\n tenantId?: string\n}\n\nexport type GeneratePresentationArgs = {\n linkedVpId: string\n}\n\nexport type LinkedVPEntry = {\n id: string\n linkedVpId: string\n tenantId?: string\n linkedVpFrom?: Date\n createdAt: Date\n}\n\nexport type LinkedVPServiceEntry = {\n id: string\n type: 'LinkedVerifiablePresentation'\n serviceEndpoint: string\n}\n\nexport type RequiredContext = IAgentContext<IIdentifierResolution & ICredentialStore & IKeyManager & VcdmCredentialPlugin & ISDJwtPlugin>\n"],"mappings":"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","names":["IsNull","Not","calculateSdHash","defaultGenerateDigest","CredentialMapper","DocumentFormat","Loggers","LOGGER_NAMESPACE","logger","Loggers","DEFAULT","get","LOGGER_NAMESPACE","CLOCK_SKEW","extractOriginalCredential","credential","udc","originalVerifiableCredential","uniformVerifiableCredential","original","createLinkedVPPresentation","holderDid","agent","debug","id","identifier","identifierManagedGet","originalCredential","documentFormat","CredentialMapper","detectDocumentType","DocumentFormat","SD_JWT_VC","decodedSdJwt","decodeSdJwtVcAsync","compactSdJwtVc","defaultGenerateDigest","hashAlg","signedPayload","_sd_alg","sdHash","calculateSdHash","kbJwtPayload","iat","Math","floor","Date","now","sd_hash","presentationResult","createSdJwtPresentation","presentation","kb","payload","presentationPayload","JSONLD","vcObject","JSON","parse","vpObject","type","verifiableCredential","holder","verifiablePresentationSP","createVerifiablePresentation","proofFormat","keyRef","kmsKeyRef","kid","MSO_MDOC","warning","vcJwt","stringify","vpPayload","iss","vp","exp","vpJwt","proof","jwt","linkedVPManagerMethods","LinkedVPManager","schema","ILinkedVPManager","methods","lvpPublishCredential","bind","lvpUnpublishCredential","lvpHasEntry","lvpGetServiceEntries","lvpGeneratePresentation","holderDids","options","args","context","digitalCredentialId","credential","agent","crsGetCredential","id","linkedVpId","Promise","reject","Error","buildLinkedVpId","tenantId","ensureLinkedVpIdUnique","publishedAt","Date","crsUpdateCredential","linkedVpFrom","createdAt","credentials","crsGetCredentials","filter","length","undefined","error","Not","IsNull","cred","map","holderDidForEntry","getHolderDid","credentialToServiceEntry","parseTenantFromLinkedVpId","holderDid","uniqueCredentials","crsGetUniqueCredentials","createLinkedVPPresentation","idx","lastIndexOf","substring","generateLinkedVpId","Math","random","toString","finalLinkedVpId","includes","getBaseUrlFromDid","startsWith","withoutPrefix","replace","parts","split","domain","shift","path","join","buildServiceEndpoint","baseUrl","cleanBaseUrl","endsWith","slice","type","serviceEndpoint"]}
|
|
1
|
+
{"version":3,"sources":["../plugin.schema.json","../src/agent/LinkedVPManager.ts","../src/services/LinkedVPService.ts","../src/types/ILinkedVPManager.ts"],"sourcesContent":["{\n \"ILinkedVPManager\": {\n \"components\": {\n \"schemas\": {\n \"GeneratePresentationArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"linkedVpId\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"linkedVpId\"\n ],\n \"additionalProperties\": false\n },\n \"LinkedVPPresentation\": {\n \"anyOf\": [\n {\n \"type\": \"string\"\n },\n {\n \"$ref\": \"#/components/schemas/Record<string,any>\"\n }\n ]\n },\n \"Record<string,any>\": {\n \"type\": \"object\"\n },\n \"GetServiceEntriesArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"tenantId\": {\n \"type\": \"string\"\n }\n },\n \"additionalProperties\": false\n },\n \"LinkedVPServiceEntry\": {\n \"type\": \"object\",\n \"properties\": {\n \"id\": {\n \"type\": \"string\"\n },\n \"type\": {\n \"type\": \"string\",\n \"const\": \"LinkedVerifiablePresentation\"\n },\n \"serviceEndpoint\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"id\",\n \"type\",\n \"serviceEndpoint\"\n ],\n \"additionalProperties\": false\n },\n \"HasLinkedVPEntryArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"linkedVpId\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"linkedVpId\"\n ],\n \"additionalProperties\": false\n },\n \"PublishCredentialArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"digitalCredentialId\": {\n \"type\": \"string\"\n },\n \"linkedVpId\": {\n \"type\": \"string\"\n },\n \"tenantId\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"digitalCredentialId\"\n ],\n \"additionalProperties\": false\n },\n \"LinkedVPEntry\": {\n \"type\": \"object\",\n \"properties\": {\n \"id\": {\n \"type\": \"string\"\n },\n \"linkedVpId\": {\n \"type\": \"string\"\n },\n \"tenantId\": {\n \"type\": \"string\"\n },\n \"linkedVpFrom\": {\n \"type\": \"string\",\n \"format\": \"date-time\"\n },\n \"createdAt\": {\n \"type\": \"string\",\n \"format\": \"date-time\"\n }\n },\n \"required\": [\n \"id\",\n \"linkedVpId\",\n \"createdAt\"\n ],\n \"additionalProperties\": false\n },\n \"UnpublishCredentialArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"linkedVpId\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"linkedVpId\"\n ],\n \"additionalProperties\": false\n }\n },\n \"methods\": {\n \"lvpGeneratePresentation\": {\n \"description\": \"Generate and return a Verifiable Presentation for a published LinkedVP This is the main endpoint handler for GET /linked-vp/\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/GeneratePresentationArgs\"\n },\n \"returnType\": {\n \"$ref\": \"#/components/schemas/LinkedVPPresentation\"\n }\n },\n \"lvpGetServiceEntries\": {\n \"description\": \"Get LinkedVP service entries for a DID to be added to a DID Document This is useful when generating DID Documents with toDidDocument\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/GetServiceEntriesArgs\"\n },\n \"returnType\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/components/schemas/LinkedVPServiceEntry\"\n }\n }\n },\n \"lvpHasEntry\": {\n \"description\": \"Check if a LinkedVP entry exists by linkedVpId\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/HasLinkedVPEntryArgs\"\n },\n \"returnType\": {\n \"type\": \"boolean\"\n }\n },\n \"lvpPublishCredential\": {\n \"description\": \"Publish a credential as a LinkedVP by adding it to the holder's DID Document\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/PublishCredentialArgs\"\n },\n \"returnType\": {\n \"$ref\": \"#/components/schemas/LinkedVPEntry\"\n }\n },\n \"lvpUnpublishCredential\": {\n \"description\": \"Unpublish a credential by removing its LinkedVP entry from the DID Document\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/UnpublishCredentialArgs\"\n },\n \"returnType\": {\n \"type\": \"boolean\"\n }\n }\n }\n }\n }\n}","import { DigitalCredential } from '@sphereon/ssi-sdk.data-store-types'\nimport { type IVerifiableCredential } from '@sphereon/ssi-types'\nimport { IAgentPlugin } from '@veramo/core'\nimport { IsNull, Not } from 'typeorm'\nimport { schema } from '../index'\nimport { createLinkedVPPresentation } from '../services/LinkedVPService'\nimport {\n GeneratePresentationArgs,\n GetServiceEntriesArgs,\n HasLinkedVPEntryArgs,\n ILinkedVPManager,\n LinkedVPEntry,\n LinkedVPPresentation,\n LinkedVPServiceEntry,\n PublishCredentialArgs,\n RequiredContext,\n UnpublishCredentialArgs,\n} from '../types'\n\n// Exposing the methods here for any REST implementation\nexport const linkedVPManagerMethods: Array<string> = [\n 'lvpPublishCredential',\n 'lvpUnpublishCredential',\n 'lvpHasEntry',\n 'lvpGetServiceEntries',\n 'lvpGeneratePresentation',\n]\n\n/**\n * {@inheritDoc ILinkedVPManager}\n */\nexport class LinkedVPManager implements IAgentPlugin {\n readonly schema = schema.ILinkedVPManager\n readonly methods: ILinkedVPManager = {\n lvpPublishCredential: this.lvpPublishCredential.bind(this),\n lvpUnpublishCredential: this.lvpUnpublishCredential.bind(this),\n lvpHasEntry: this.lvpHasEntry.bind(this),\n lvpGetServiceEntries: this.lvpGetServiceEntries.bind(this),\n lvpGeneratePresentation: this.lvpGeneratePresentation.bind(this),\n }\n\n private async lvpPublishCredential(args: PublishCredentialArgs, context: RequiredContext): Promise<LinkedVPEntry> {\n const { digitalCredentialId } = args\n\n const credential: DigitalCredential = await context.agent.crsGetCredential({ id: digitalCredentialId })\n\n if (credential.linkedVpId) {\n return Promise.reject(new Error(`Credential ${digitalCredentialId} is already published with linkedVpId ${credential.linkedVpId}`))\n }\n\n const linkedVpId = this.buildLinkedVpId(args.linkedVpId, credential.tenantId)\n\n await this.ensureLinkedVpIdUnique(linkedVpId, context, credential.tenantId)\n\n const publishAt = args.linkedVpFrom ?? new Date()\n await context.agent.crsUpdateCredential({\n id: digitalCredentialId,\n linkedVpId,\n linkedVpFrom: publishAt,\n linkedVpUntil: args.linkedVpUntil,\n })\n\n return {\n id: credential.id,\n linkedVpId,\n tenantId: credential.tenantId,\n linkedVpFrom: publishAt,\n linkedVpUntil: args.linkedVpUntil,\n createdAt: credential.createdAt,\n }\n }\n\n private async lvpUnpublishCredential(args: UnpublishCredentialArgs, context: RequiredContext): Promise<boolean> {\n const { linkedVpId } = args\n\n // Find credential by linkedVpId and tenantId\n const credentials = await context.agent.crsGetCredentials({\n filter: [{ linkedVpId }],\n })\n if (credentials.length === 0) {\n return Promise.reject(Error(`No credential found with linkedVpId ${linkedVpId}`))\n }\n\n const credential = credentials[0]\n await context.agent.crsUpdateCredential({\n id: credential.id,\n linkedVpId: undefined,\n linkedVpFrom: undefined,\n })\n\n return true\n }\n\n private async lvpHasEntry(args: HasLinkedVPEntryArgs, context: RequiredContext): Promise<boolean> {\n const { linkedVpId } = args\n\n try {\n const credentials = await context.agent.crsGetCredentials({\n filter: [{ linkedVpId }],\n })\n return credentials.length > 0\n } catch (error) {\n return false\n }\n }\n\n private async lvpGetServiceEntries(args: GetServiceEntriesArgs, context: RequiredContext): Promise<Array<LinkedVPServiceEntry>> {\n const { tenantId, subjectDid } = args\n\n // Get all published credentials (credentials with linkedVpId set)\n const filter: any = { linkedVpId: Not(IsNull()) }\n if (tenantId) {\n filter.tenantId = tenantId\n }\n if (subjectDid) {\n filter.subjectCorrelationId = subjectDid\n }\n\n const credentials = await context.agent.crsGetCredentials({\n filter: [filter],\n })\n\n return credentials\n .flatMap((cred) => {\n const uniformDocument = JSON.parse(cred.uniformDocument) as IVerifiableCredential\n const holderDidForEntry = this.getHolderDid(uniformDocument)\n return holderDidForEntry && holderDidForEntry.startsWith('did:web') ? [this.credentialToServiceEntry(cred, holderDidForEntry)] : []\n })\n }\n\n private async lvpGeneratePresentation(args: GeneratePresentationArgs, context: RequiredContext): Promise<LinkedVPPresentation> {\n const { linkedVpId } = args\n const tenantId = this.parseTenantFromLinkedVpId(linkedVpId)\n\n const uniqueCredentials = await context.agent.crsGetUniqueCredentials({\n filter: [\n {\n linkedVpId: args.linkedVpId,\n linkedVpUntil: args.linkedVpUntil,\n ...(tenantId && { tenantId }),\n },\n ],\n })\n if (uniqueCredentials.length === 0) {\n return Promise.reject(Error(`No published credentials found for linkedVpId ${linkedVpId}`))\n }\n if (uniqueCredentials.length > 1) {\n return Promise.reject(Error(`Multiple credentials found for linkedVpId ${linkedVpId}`))\n }\n\n const uniqueDigitalCredential = uniqueCredentials[0]\n if (!uniqueDigitalCredential.uniformVerifiableCredential) {\n return Promise.reject(Error(`uniformVerifiableCredential could not be found for credential ${uniqueDigitalCredential.digitalCredential.id}`))\n }\n const holderDid = this.getHolderDid(uniqueDigitalCredential.uniformVerifiableCredential)\n if (!holderDid) {\n return Promise.reject(Error(`Could not extract the holder did:web from cnf nor the credentialSubject id`))\n }\n\n // Generate the Verifiable Presentation with all published credentials\n return createLinkedVPPresentation(holderDid, uniqueDigitalCredential, context.agent)\n }\n\n private getHolderDid(uniformDocument: IVerifiableCredential): string | undefined {\n // Determine holder DID for identifier resolution\n if ('cnf' in uniformDocument && 'jwk' in uniformDocument.cnf && 'kid' in uniformDocument.cnf.jwk) {\n return uniformDocument.cnf.jwk.kid.split('#')[0]\n }\n\n if ('credentialSubject' in uniformDocument) {\n const credentialSubject = Array.isArray(uniformDocument.credentialSubject)\n ? uniformDocument.credentialSubject[0]\n : uniformDocument.credentialSubject\n if ('id' in credentialSubject && credentialSubject.id) {\n if (credentialSubject.id.startsWith('did:web')) {\n return credentialSubject.id\n }\n }\n }\n\n return undefined\n }\n\n private parseTenantFromLinkedVpId(linkedVpId: string): string | undefined {\n const idx = linkedVpId.lastIndexOf('@')\n return idx === -1 ? undefined : linkedVpId.substring(idx + 1)\n }\n\n private generateLinkedVpId(): string {\n return `lvp-${Math.random().toString(36).substring(2, 15)}`\n }\n\n private async ensureLinkedVpIdUnique(linkedVpId: string, context: RequiredContext, tenantId?: string): Promise<void> {\n const credentials = await context.agent.crsGetCredentials({\n filter: [{ linkedVpId, ...(tenantId && { tenantId }) }],\n })\n\n if (credentials.length > 0) {\n throw new Error(`LinkedVP ID ${linkedVpId} already exists${tenantId ? ` for tenant ${tenantId}` : ''}`)\n }\n }\n\n private buildLinkedVpId(linkedVpId: string | undefined, tenantId: string | undefined) {\n let finalLinkedVpId = linkedVpId || this.generateLinkedVpId()\n\n // Validate that user-provided ID doesn't contain @ char reserved for tenant id separator\n if (linkedVpId && linkedVpId.includes('@')) {\n throw new Error(`LinkedVP ID cannot contain '@' character as it is reserved for tenant separation`)\n }\n\n // Append tenantId if provided\n if (tenantId && tenantId !== '') {\n finalLinkedVpId = `${finalLinkedVpId}@${tenantId}`\n }\n return finalLinkedVpId\n }\n\n private getBaseUrlFromDid(holderDid: string): string {\n if (!holderDid.startsWith('did:web:')) {\n throw new Error(`Invalid DID: ${holderDid}, must be did:web`)\n }\n\n const withoutPrefix = holderDid.replace('did:web:', '') // example.com:tenants:tenant1\n const parts = withoutPrefix.split(':')\n const domain = parts.shift()! // example.com\n const path = parts.join('/') // tenants/tenant1\n\n return path\n ? `https://${domain}/${path}` // https://example.com/tenants/tenant1\n : `https://${domain}` // https://example.com\n }\n\n private buildServiceEndpoint(holderDid: string, linkedVpId: string): string {\n const baseUrl = this.getBaseUrlFromDid(holderDid)\n const cleanBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl\n return `${cleanBaseUrl}/linked-vp/${linkedVpId}`\n }\n\n private credentialToServiceEntry(credential: DigitalCredential, holderDid: string): LinkedVPServiceEntry {\n if (!credential.linkedVpId) {\n throw new Error(`Credential ${credential.id} does not have a linkedVpId`)\n }\n\n return {\n id: `${holderDid}#${credential.linkedVpId}`,\n type: 'LinkedVerifiablePresentation',\n serviceEndpoint: this.buildServiceEndpoint(holderDid, credential.linkedVpId),\n }\n }\n}\n","import { UniqueDigitalCredential } from '@sphereon/ssi-sdk.credential-store'\nimport { calculateSdHash, defaultGenerateDigest, PartialSdJwtKbJwt } from '@sphereon/ssi-sdk.sd-jwt'\n\nimport {\n CredentialMapper,\n DocumentFormat,\n Loggers,\n OriginalVerifiableCredential,\n SdJwtDecodedVerifiableCredential,\n WrappedVerifiableCredential,\n} from '@sphereon/ssi-types'\nimport { LinkedVPPresentation, LOGGER_NAMESPACE, RequiredContext } from '../types'\n\nconst logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE)\nconst CLOCK_SKEW = 120 // TODO make adjustable?\n\n/**\n * Creates a Verifiable Presentation for LinkedVP publishing\n * Contains multiple credentials in a single JWT VP\n * No nonce or audience since this is for publishing, not responding to verification\n */\nexport async function createLinkedVPPresentation(\n holderDid: string,\n credential: UniqueDigitalCredential,\n agent: RequiredContext['agent'],\n): Promise<LinkedVPPresentation> {\n logger.debug(`Creating LinkedVP presentation for ${holderDid} of credential ${credential.id}`)\n\n const originalCredential = extractOriginalCredential(credential)\n const documentFormat = CredentialMapper.detectDocumentType(originalCredential)\n\n switch (documentFormat) {\n case DocumentFormat.SD_JWT_VC: {\n return createSdJwtPresentation(originalCredential, agent)\n }\n case DocumentFormat.JSONLD: {\n return createJsonLdPresentation(holderDid, originalCredential, agent)\n }\n case DocumentFormat.MSO_MDOC: {\n return createMdocPresentation(originalCredential)\n }\n default: {\n return createJwtPresentation(holderDid, originalCredential, agent)\n }\n }\n}\n\n/**\n * Extracts the original credential from various wrapper types\n */\nfunction extractOriginalCredential(\n credential: UniqueDigitalCredential | WrappedVerifiableCredential | OriginalVerifiableCredential,\n): OriginalVerifiableCredential {\n if (typeof credential === 'string') {\n return credential\n }\n\n if ('digitalCredential' in credential) {\n const udc = credential as UniqueDigitalCredential\n if (udc.originalVerifiableCredential) {\n return udc.originalVerifiableCredential\n }\n return udc.uniformVerifiableCredential as OriginalVerifiableCredential\n }\n\n if ('original' in credential) {\n return credential.original\n }\n\n return credential as OriginalVerifiableCredential\n}\n\n/**\n * Creates an SD-JWT presentation with KB-JWT\n */\nasync function createSdJwtPresentation(\n originalCredential: OriginalVerifiableCredential,\n agent: RequiredContext['agent'],\n): Promise<LinkedVPPresentation> {\n // SD-JWT with KB-JWT\n const decodedSdJwt = await CredentialMapper.decodeSdJwtVcAsync(\n typeof originalCredential === 'string' ? originalCredential : (originalCredential as SdJwtDecodedVerifiableCredential).compactSdJwtVc,\n defaultGenerateDigest,\n )\n\n const hashAlg = decodedSdJwt.signedPayload._sd_alg ?? 'sha-256'\n const sdHash = calculateSdHash(decodedSdJwt.compactSdJwtVc, hashAlg, defaultGenerateDigest)\n const kbJwtPayload: PartialSdJwtKbJwt['payload'] = {\n iat: Math.floor(Date.now() / 1000 - CLOCK_SKEW),\n sd_hash: sdHash,\n }\n\n const presentationResult = await agent.createSdJwtPresentation({\n presentation: decodedSdJwt.compactSdJwtVc,\n kb: {\n payload: kbJwtPayload as any, // FIXME? (typescript seems impossible)\n },\n })\n\n return {\n documentFormat: DocumentFormat.SD_JWT_VC,\n presentationPayload: presentationResult.presentation,\n }\n}\n\n/**\n * Creates a JSON-LD presentation with proof\n */\nasync function createJsonLdPresentation(\n holderDid: string,\n originalCredential: OriginalVerifiableCredential,\n agent: RequiredContext['agent'],\n): Promise<LinkedVPPresentation> {\n // JSON-LD VC - create JSON-LD VP with challenge and domain in proof\n const vcObject = typeof originalCredential === 'string' ? JSON.parse(originalCredential) : originalCredential\n\n const vpObject = {\n '@context': ['https://www.w3.org/2018/credentials/v1'],\n type: ['VerifiablePresentation'],\n verifiableCredential: [vcObject],\n holder: holderDid,\n }\n\n const identifier = await agent.identifierManagedGet({ identifier: holderDid })\n\n // Create JSON-LD VP with proof\n const verifiablePresentationSP = await agent.createVerifiablePresentation({\n presentation: vpObject,\n proofFormat: 'lds',\n keyRef: identifier.kmsKeyRef || identifier.kid,\n })\n return {\n documentFormat: DocumentFormat.JSONLD,\n presentationPayload: verifiablePresentationSP,\n }\n}\n\n/**\n * Creates an ISO mdoc presentation (basic support)\n */\nasync function createMdocPresentation(originalCredential: OriginalVerifiableCredential): Promise<LinkedVPPresentation> {\n // ISO mdoc - create mdoc VP token\n // This is a placeholder implementation\n // Full implementation would require:\n // 1. Decode the mdoc using CredentialMapper or mdoc utilities\n // 2. Build proper mdoc VP token with session transcript\n // 3. Include nonce/audience in the session transcript\n logger.warning('mso_mdoc format has basic support - production use requires proper mdoc VP token implementation')\n\n return {\n documentFormat: DocumentFormat.MSO_MDOC,\n presentationPayload: originalCredential,\n }\n}\n\n/**\n * Creates a JWT presentation\n */\nasync function createJwtPresentation(\n holderDid: string,\n originalCredential: OriginalVerifiableCredential,\n agent: RequiredContext['agent'],\n): Promise<LinkedVPPresentation> {\n // JWT VC - create JWT VP with nonce and aud in payload\n const vcJwt = typeof originalCredential === 'string' ? originalCredential : JSON.stringify(originalCredential)\n\n const identifier = await agent.identifierManagedGet({ identifier: holderDid })\n\n // Create VP JWT using agent method\n const vpPayload = {\n iss: holderDid,\n vp: {\n '@context': ['https://www.w3.org/2018/credentials/v1'],\n type: ['VerifiablePresentation'],\n holder: holderDid,\n verifiableCredential: [vcJwt],\n },\n iat: Math.floor(Date.now() / 1000 - CLOCK_SKEW),\n exp: Math.floor(Date.now() / 1000 + 600 + CLOCK_SKEW), // 10 minutes\n }\n\n // Use the agent's JWT creation capability\n const vpJwt = await agent.createVerifiablePresentation({\n presentation: vpPayload.vp,\n proofFormat: 'jwt',\n keyRef: identifier.kmsKeyRef || identifier.kid,\n })\n\n return {\n documentFormat: DocumentFormat.JWT,\n presentationPayload: (vpJwt.proof && 'jwt' in vpJwt.proof && vpJwt.proof.jwt) || vpJwt,\n }\n}\n","import { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'\nimport { ICredentialStore } from '@sphereon/ssi-sdk.credential-store'\nimport { VcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'\nimport { ISDJwtPlugin } from '@sphereon/ssi-sdk.sd-jwt'\nimport { DocumentFormat } from '@sphereon/ssi-types'\nimport { IAgentContext, IPluginMethodMap } from '@veramo/core'\nimport { IKeyManager } from '@veramo/core/src/types/IKeyManager'\n\nexport const LOGGER_NAMESPACE = 'sphereon:linked-vp'\n\nexport type LinkedVPPresentation = {\n documentFormat: DocumentFormat\n presentationPayload: string | Record<string, any>\n}\n\nexport interface ILinkedVPManager extends IPluginMethodMap {\n /**\n * Publish a credential as a LinkedVP by adding it to the holder's DID Document\n * @param args - Publication arguments including credential ID and scope configuration\n * @param context - Agent context\n */\n lvpPublishCredential(args: PublishCredentialArgs, context: RequiredContext): Promise<LinkedVPEntry>\n\n /**\n * Unpublish a credential by removing its LinkedVP entry from the DID Document\n * @param args - Unpublish arguments\n * @param context - Agent context\n */\n lvpUnpublishCredential(args: UnpublishCredentialArgs, context: RequiredContext): Promise<boolean>\n\n /**\n * Check if a LinkedVP entry exists by linkedVpId\n * @param args - Query arguments\n * @param context - Agent context\n */\n lvpHasEntry(args: HasLinkedVPEntryArgs, context: RequiredContext): Promise<boolean>\n\n /**\n * Get LinkedVP service entries for a DID to be added to a DID Document\n * This is useful when generating DID Documents with toDidDocument\n * @param args - Query arguments for the DID\n * @param context - Agent context\n */\n lvpGetServiceEntries(args: GetServiceEntriesArgs, context: RequiredContext): Promise<Array<LinkedVPServiceEntry>>\n\n /**\n * Generate and return a Verifiable Presentation for a published LinkedVP\n * This is the main endpoint handler for GET /linked-vp/{linkedVpId}\n * @param args - Generation arguments\n * @param context - Agent context\n */\n lvpGeneratePresentation(args: GeneratePresentationArgs, context: RequiredContext): Promise<LinkedVPPresentation>\n}\n\nexport type PublishCredentialArgs = {\n digitalCredentialId: string\n linkedVpId?: string // Optional: if not provided, will be auto-generated\n linkedVpFrom?: Date\n linkedVpUntil?: Date\n}\n\nexport type UnpublishCredentialArgs = {\n linkedVpId: string\n}\n\nexport type HasLinkedVPEntryArgs = {\n linkedVpId: string\n}\n\nexport type GetServiceEntriesArgs = {\n subjectDid?: string\n tenantId?: string\n}\n\nexport type GeneratePresentationArgs = {\n linkedVpId: string\n linkedVpUntil?: Date\n}\n\nexport type LinkedVPEntry = {\n id: string\n linkedVpId: string\n linkedVpFrom?: Date\n linkedVpUntil?: Date\n tenantId?: string\n createdAt: Date\n}\n\nexport type LinkedVPServiceEntry = {\n id: string\n type: 'LinkedVerifiablePresentation'\n serviceEndpoint: string\n}\n\nexport type RequiredContext = IAgentContext<IIdentifierResolution & ICredentialStore & IKeyManager & VcdmCredentialPlugin & ISDJwtPlugin>\n"],"mappings":"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","names":["IsNull","Not","calculateSdHash","defaultGenerateDigest","CredentialMapper","DocumentFormat","Loggers","LOGGER_NAMESPACE","logger","Loggers","DEFAULT","get","LOGGER_NAMESPACE","CLOCK_SKEW","createLinkedVPPresentation","holderDid","credential","agent","debug","id","originalCredential","extractOriginalCredential","documentFormat","CredentialMapper","detectDocumentType","DocumentFormat","SD_JWT_VC","createSdJwtPresentation","JSONLD","createJsonLdPresentation","MSO_MDOC","createMdocPresentation","createJwtPresentation","udc","originalVerifiableCredential","uniformVerifiableCredential","original","decodedSdJwt","decodeSdJwtVcAsync","compactSdJwtVc","defaultGenerateDigest","hashAlg","signedPayload","_sd_alg","sdHash","calculateSdHash","kbJwtPayload","iat","Math","floor","Date","now","sd_hash","presentationResult","presentation","kb","payload","presentationPayload","vcObject","JSON","parse","vpObject","type","verifiableCredential","holder","identifier","identifierManagedGet","verifiablePresentationSP","createVerifiablePresentation","proofFormat","keyRef","kmsKeyRef","kid","warning","vcJwt","stringify","vpPayload","iss","vp","exp","vpJwt","JWT","proof","jwt","linkedVPManagerMethods","LinkedVPManager","schema","ILinkedVPManager","methods","lvpPublishCredential","bind","lvpUnpublishCredential","lvpHasEntry","lvpGetServiceEntries","lvpGeneratePresentation","args","context","digitalCredentialId","credential","agent","crsGetCredential","id","linkedVpId","Promise","reject","Error","buildLinkedVpId","tenantId","ensureLinkedVpIdUnique","publishAt","linkedVpFrom","Date","crsUpdateCredential","linkedVpUntil","createdAt","credentials","crsGetCredentials","filter","length","undefined","error","subjectDid","Not","IsNull","subjectCorrelationId","flatMap","cred","uniformDocument","JSON","parse","holderDidForEntry","getHolderDid","startsWith","credentialToServiceEntry","parseTenantFromLinkedVpId","uniqueCredentials","crsGetUniqueCredentials","uniqueDigitalCredential","uniformVerifiableCredential","digitalCredential","holderDid","createLinkedVPPresentation","cnf","jwk","kid","split","credentialSubject","Array","isArray","idx","lastIndexOf","substring","generateLinkedVpId","Math","random","toString","finalLinkedVpId","includes","getBaseUrlFromDid","withoutPrefix","replace","parts","domain","shift","path","join","buildServiceEndpoint","baseUrl","cleanBaseUrl","endsWith","slice","type","serviceEndpoint"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sphereon/ssi-sdk.linked-vp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.1-feature.SSISDK.70.integrate.digidentity.55+fc4df611",
|
|
4
4
|
"source": "src/index.ts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -27,13 +27,13 @@
|
|
|
27
27
|
"generate-plugin-schema": "tsx ../../packages/dev/bin/sphereon.js dev generate-plugin-schema"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@sphereon/ssi-sdk-ext.identifier-resolution": "0.
|
|
31
|
-
"@sphereon/ssi-sdk.credential-store": "0.
|
|
32
|
-
"@sphereon/ssi-sdk.credential-vcdm": "0.
|
|
33
|
-
"@sphereon/ssi-sdk.data-store": "0.
|
|
34
|
-
"@sphereon/ssi-sdk.data-store-types": "0.
|
|
35
|
-
"@sphereon/ssi-sdk.sd-jwt": "0.
|
|
36
|
-
"@sphereon/ssi-types": "0.
|
|
30
|
+
"@sphereon/ssi-sdk-ext.identifier-resolution": "0.36.1-feature.SSISDK.70.integrate.digidentity.55+fc4df611",
|
|
31
|
+
"@sphereon/ssi-sdk.credential-store": "0.36.1-feature.SSISDK.70.integrate.digidentity.55+fc4df611",
|
|
32
|
+
"@sphereon/ssi-sdk.credential-vcdm": "0.36.1-feature.SSISDK.70.integrate.digidentity.55+fc4df611",
|
|
33
|
+
"@sphereon/ssi-sdk.data-store": "0.36.1-feature.SSISDK.70.integrate.digidentity.55+fc4df611",
|
|
34
|
+
"@sphereon/ssi-sdk.data-store-types": "0.36.1-feature.SSISDK.70.integrate.digidentity.55+fc4df611",
|
|
35
|
+
"@sphereon/ssi-sdk.sd-jwt": "0.36.1-feature.SSISDK.70.integrate.digidentity.55+fc4df611",
|
|
36
|
+
"@sphereon/ssi-types": "0.36.1-feature.SSISDK.70.integrate.digidentity.55+fc4df611",
|
|
37
37
|
"@veramo/utils": "4.2.0",
|
|
38
38
|
"cross-fetch": "^4.1.0",
|
|
39
39
|
"dcql": "1.0.1",
|
|
@@ -42,10 +42,11 @@
|
|
|
42
42
|
"uint8arrays": "3.1.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@
|
|
46
|
-
"@sphereon/ssi-sdk-ext.
|
|
47
|
-
"@sphereon/ssi-sdk.
|
|
48
|
-
"@sphereon/ssi-sdk.
|
|
45
|
+
"@sd-jwt/types": "^0.15.0",
|
|
46
|
+
"@sphereon/ssi-sdk-ext.key-manager": "0.36.1-feature.SSISDK.70.integrate.digidentity.55+fc4df611",
|
|
47
|
+
"@sphereon/ssi-sdk-ext.kms-local": "0.36.1-feature.SSISDK.70.integrate.digidentity.55+fc4df611",
|
|
48
|
+
"@sphereon/ssi-sdk.agent-config": "0.36.1-feature.SSISDK.70.integrate.digidentity.55+fc4df611",
|
|
49
|
+
"@sphereon/ssi-sdk.credential-vcdm1-jwt-provider": "0.36.1-feature.SSISDK.70.integrate.digidentity.55+fc4df611",
|
|
49
50
|
"@veramo/data-store": "4.2.0",
|
|
50
51
|
"@veramo/did-manager": "4.2.0",
|
|
51
52
|
"@veramo/did-provider-web": "4.2.0",
|
|
@@ -74,8 +75,7 @@
|
|
|
74
75
|
"Sphereon",
|
|
75
76
|
"SSI",
|
|
76
77
|
"Veramo",
|
|
77
|
-
"
|
|
78
|
-
"PD Manager"
|
|
78
|
+
"Linked VP Manager"
|
|
79
79
|
],
|
|
80
|
-
"gitHead": "
|
|
80
|
+
"gitHead": "fc4df611242812613a69238d3d03452395505b64"
|
|
81
81
|
}
|
|
@@ -29,7 +29,7 @@ async function createTestCredential(agent: ConfiguredAgent, tenantId: string) {
|
|
|
29
29
|
credential: {
|
|
30
30
|
credentialRole: CredentialRole.HOLDER,
|
|
31
31
|
rawDocument: JSON.stringify(mockVC),
|
|
32
|
-
issuerCorrelationType: 'DID'
|
|
32
|
+
issuerCorrelationType: 'DID',
|
|
33
33
|
issuerCorrelationId: 'did:web:issuer.com',
|
|
34
34
|
kmsKeyRef: 'mock-key-ref',
|
|
35
35
|
identifierMethod: 'did:web',
|
|
@@ -63,7 +63,7 @@ export default (testContext: {
|
|
|
63
63
|
digitalCredentialId: credentialId,
|
|
64
64
|
})
|
|
65
65
|
|
|
66
|
-
expect(result.linkedVpId).toMatch(/^lvp
|
|
66
|
+
expect(result.linkedVpId).toMatch(/^lvp-[a-z0-9]+@tenant1$/)
|
|
67
67
|
})
|
|
68
68
|
|
|
69
69
|
it('should publish credential with custom linkedVpId AND append tenantId', async () => {
|
|
@@ -127,6 +127,15 @@ export default (testContext: {
|
|
|
127
127
|
})
|
|
128
128
|
|
|
129
129
|
it('should get service entries for default tenant (no tenantId param)', async () => {
|
|
130
|
+
// Clean up any existing published credentials first
|
|
131
|
+
const existingEntries = await agent.lvpGetServiceEntries({})
|
|
132
|
+
for (const entry of existingEntries) {
|
|
133
|
+
const linkedVpId = entry.id.split('#')[1]
|
|
134
|
+
await agent.lvpUnpublishCredential({ linkedVpId }).catch(() => {
|
|
135
|
+
// Ignore errors if already unpublished
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
|
|
130
139
|
const id1 = await createTestCredential(agent, tenantId)
|
|
131
140
|
const id2 = await createTestCredential(agent, tenantId)
|
|
132
141
|
|
|
@@ -135,17 +144,18 @@ export default (testContext: {
|
|
|
135
144
|
|
|
136
145
|
const entries = await agent.lvpGetServiceEntries({})
|
|
137
146
|
|
|
147
|
+
expect(entries).toHaveLength(2)
|
|
138
148
|
expect(entries).toEqual(
|
|
139
149
|
expect.arrayContaining([
|
|
140
150
|
{
|
|
141
|
-
id: `${
|
|
151
|
+
id: `${holderDid}#service1@tenant1`,
|
|
142
152
|
type: 'LinkedVerifiablePresentation',
|
|
143
|
-
serviceEndpoint: `https://example.com/
|
|
153
|
+
serviceEndpoint: `https://example.com/linked-vp/service1@tenant1`,
|
|
144
154
|
},
|
|
145
155
|
{
|
|
146
|
-
id: `${
|
|
156
|
+
id: `${holderDid}#service2@tenant1`,
|
|
147
157
|
type: 'LinkedVerifiablePresentation',
|
|
148
|
-
serviceEndpoint: `https://example.com/
|
|
158
|
+
serviceEndpoint: `https://example.com/linked-vp/service2@tenant1`,
|
|
149
159
|
},
|
|
150
160
|
]),
|
|
151
161
|
)
|