@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.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 createLinkedVPPresentation(holderDid, credential, agent) {
220
- logger.debug(`Creating LinkedVP presentation for ${holderDid} of credential ${credential.id}`);
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 originalCredential = extractOriginalCredential(credential);
225
- const documentFormat = CredentialMapper.detectDocumentType(originalCredential);
226
- switch (documentFormat) {
227
- case DocumentFormat.SD_JWT_VC: {
228
- const decodedSdJwt = await CredentialMapper.decodeSdJwtVcAsync(typeof originalCredential === "string" ? originalCredential : originalCredential.compactSdJwtVc, defaultGenerateDigest);
229
- const hashAlg = decodedSdJwt.signedPayload._sd_alg ?? "sha-256";
230
- const sdHash = calculateSdHash(decodedSdJwt.compactSdJwtVc, hashAlg, defaultGenerateDigest);
231
- const kbJwtPayload = {
232
- iat: Math.floor(Date.now() / 1e3 - CLOCK_SKEW),
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(createLinkedVPPresentation, "createLinkedVPPresentation");
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 publishedAt = /* @__PURE__ */ new Date();
359
+ const publishAt = args.linkedVpFrom ?? /* @__PURE__ */ new Date();
345
360
  await context.agent.crsUpdateCredential({
346
361
  id: digitalCredentialId,
347
362
  linkedVpId,
348
- linkedVpFrom: publishedAt
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: publishedAt,
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.filter((cred) => cred.linkedVpId !== void 0 && cred.linkedVpId !== null).map((cred) => {
407
- const holderDidForEntry = this.getHolderDid(cred.tenantId);
408
- return this.credentialToServiceEntry(cred, holderDidForEntry);
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
- return createLinkedVPPresentation(holderDid, uniqueCredentials[0], context.agent);
432
- }
433
- getHolderDid(tenantId) {
434
- const holderDid = this.holderDids[tenantId ?? "default"];
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
- throw Error(`No holder did supplied for tenant ${tenantId ?? "default"}`);
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
- return holderDid;
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 (tenantId && tenantId !== "" && !finalLinkedVpId.includes("@")) {
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.34.1-feature.SSISDK.82.linkedVP.328+20af9c29",
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.34.1-feature.SSISDK.82.linkedVP.328+20af9c29",
31
- "@sphereon/ssi-sdk.credential-store": "0.34.1-feature.SSISDK.82.linkedVP.328+20af9c29",
32
- "@sphereon/ssi-sdk.credential-vcdm": "0.34.1-feature.SSISDK.82.linkedVP.328+20af9c29",
33
- "@sphereon/ssi-sdk.data-store": "0.34.1-feature.SSISDK.82.linkedVP.328+20af9c29",
34
- "@sphereon/ssi-sdk.data-store-types": "0.34.1-feature.SSISDK.82.linkedVP.328+20af9c29",
35
- "@sphereon/ssi-sdk.sd-jwt": "0.34.1-feature.SSISDK.82.linkedVP.328+20af9c29",
36
- "@sphereon/ssi-types": "0.34.1-feature.SSISDK.82.linkedVP.328+20af9c29",
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
- "@sphereon/ssi-sdk-ext.key-manager": "0.34.1-feature.SSISDK.82.linkedVP.328+20af9c29",
46
- "@sphereon/ssi-sdk-ext.kms-local": "0.34.1-feature.SSISDK.82.linkedVP.328+20af9c29",
47
- "@sphereon/ssi-sdk.agent-config": "0.34.1-feature.SSISDK.82.linkedVP.328+20af9c29",
48
- "@sphereon/ssi-sdk.credential-vcdm1-jwt-provider": "0.34.1-feature.SSISDK.82.linkedVP.328+20af9c29",
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
- "Presentation Defintion Manager",
78
- "PD Manager"
78
+ "Linked VP Manager"
79
79
  ],
80
- "gitHead": "20af9c298b5d6764da6c804d1c6d8a758b710513"
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' as any,
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-\d+-[a-z0-9]+@tenant1$/)
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: `${holderDidWithTenant}#service1@tenant1`,
151
+ id: `${holderDid}#service1@tenant1`,
142
152
  type: 'LinkedVerifiablePresentation',
143
- serviceEndpoint: `https://example.com/tenants/tenant1/linked-vp/service1@tenant1`,
153
+ serviceEndpoint: `https://example.com/linked-vp/service1@tenant1`,
144
154
  },
145
155
  {
146
- id: `${holderDidWithTenant}#service2@tenant1`,
156
+ id: `${holderDid}#service2@tenant1`,
147
157
  type: 'LinkedVerifiablePresentation',
148
- serviceEndpoint: `https://example.com/tenants/tenant1/linked-vp/service2@tenant1`,
158
+ serviceEndpoint: `https://example.com/linked-vp/service2@tenant1`,
149
159
  },
150
160
  ]),
151
161
  )