@sphereon/ssi-sdk.linked-vp 0.36.1-feature.SSISDK.82.and.SSISDK.70.37 → 0.36.1-next.39

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 CHANGED
@@ -226,6 +226,26 @@ var LOGGER_NAMESPACE = "sphereon:linked-vp";
226
226
  // src/services/LinkedVPService.ts
227
227
  var logger = import_ssi_types.Loggers.DEFAULT.get(LOGGER_NAMESPACE);
228
228
  var CLOCK_SKEW = 120;
229
+ async function createLinkedVPPresentation(holderDid, credential, agent) {
230
+ logger.debug(`Creating LinkedVP presentation for ${holderDid} of credential ${credential.id}`);
231
+ const originalCredential = extractOriginalCredential(credential);
232
+ const documentFormat = import_ssi_types.CredentialMapper.detectDocumentType(originalCredential);
233
+ switch (documentFormat) {
234
+ case import_ssi_types.DocumentFormat.SD_JWT_VC: {
235
+ return createSdJwtPresentation(originalCredential, agent);
236
+ }
237
+ case import_ssi_types.DocumentFormat.JSONLD: {
238
+ return createJsonLdPresentation(holderDid, originalCredential, agent);
239
+ }
240
+ case import_ssi_types.DocumentFormat.MSO_MDOC: {
241
+ return createMdocPresentation(originalCredential);
242
+ }
243
+ default: {
244
+ return createJwtPresentation(holderDid, originalCredential, agent);
245
+ }
246
+ }
247
+ }
248
+ __name(createLinkedVPPresentation, "createLinkedVPPresentation");
229
249
  function extractOriginalCredential(credential) {
230
250
  if (typeof credential === "string") {
231
251
  return credential;
@@ -243,96 +263,95 @@ function extractOriginalCredential(credential) {
243
263
  return credential;
244
264
  }
245
265
  __name(extractOriginalCredential, "extractOriginalCredential");
246
- async function createLinkedVPPresentation(holderDid, credential, agent) {
247
- logger.debug(`Creating LinkedVP presentation for ${holderDid} of credential ${credential.id}`);
266
+ async function createSdJwtPresentation(originalCredential, agent) {
267
+ const decodedSdJwt = await import_ssi_types.CredentialMapper.decodeSdJwtVcAsync(typeof originalCredential === "string" ? originalCredential : originalCredential.compactSdJwtVc, import_ssi_sdk.defaultGenerateDigest);
268
+ const hashAlg = decodedSdJwt.signedPayload._sd_alg ?? "sha-256";
269
+ const sdHash = (0, import_ssi_sdk.calculateSdHash)(decodedSdJwt.compactSdJwtVc, hashAlg, import_ssi_sdk.defaultGenerateDigest);
270
+ const kbJwtPayload = {
271
+ iat: Math.floor(Date.now() / 1e3 - CLOCK_SKEW),
272
+ sd_hash: sdHash
273
+ };
274
+ const presentationResult = await agent.createSdJwtPresentation({
275
+ presentation: decodedSdJwt.compactSdJwtVc,
276
+ kb: {
277
+ payload: kbJwtPayload
278
+ }
279
+ });
280
+ return {
281
+ documentFormat: import_ssi_types.DocumentFormat.SD_JWT_VC,
282
+ presentationPayload: presentationResult.presentation
283
+ };
284
+ }
285
+ __name(createSdJwtPresentation, "createSdJwtPresentation");
286
+ async function createJsonLdPresentation(holderDid, originalCredential, agent) {
287
+ const vcObject = typeof originalCredential === "string" ? JSON.parse(originalCredential) : originalCredential;
288
+ const vpObject = {
289
+ "@context": [
290
+ "https://www.w3.org/2018/credentials/v1"
291
+ ],
292
+ type: [
293
+ "VerifiablePresentation"
294
+ ],
295
+ verifiableCredential: [
296
+ vcObject
297
+ ],
298
+ holder: holderDid
299
+ };
248
300
  const identifier = await agent.identifierManagedGet({
249
301
  identifier: holderDid
250
302
  });
251
- const originalCredential = extractOriginalCredential(credential);
252
- const documentFormat = import_ssi_types.CredentialMapper.detectDocumentType(originalCredential);
253
- switch (documentFormat) {
254
- case import_ssi_types.DocumentFormat.SD_JWT_VC: {
255
- const decodedSdJwt = await import_ssi_types.CredentialMapper.decodeSdJwtVcAsync(typeof originalCredential === "string" ? originalCredential : originalCredential.compactSdJwtVc, import_ssi_sdk.defaultGenerateDigest);
256
- const hashAlg = decodedSdJwt.signedPayload._sd_alg ?? "sha-256";
257
- const sdHash = (0, import_ssi_sdk.calculateSdHash)(decodedSdJwt.compactSdJwtVc, hashAlg, import_ssi_sdk.defaultGenerateDigest);
258
- const kbJwtPayload = {
259
- iat: Math.floor(Date.now() / 1e3 - CLOCK_SKEW),
260
- sd_hash: sdHash
261
- };
262
- const presentationResult = await agent.createSdJwtPresentation({
263
- presentation: decodedSdJwt.compactSdJwtVc,
264
- kb: {
265
- payload: kbJwtPayload
266
- }
267
- });
268
- return {
269
- documentFormat,
270
- presentationPayload: presentationResult.presentation
271
- };
272
- }
273
- case import_ssi_types.DocumentFormat.JSONLD: {
274
- const vcObject = typeof originalCredential === "string" ? JSON.parse(originalCredential) : originalCredential;
275
- const vpObject = {
276
- "@context": [
277
- "https://www.w3.org/2018/credentials/v1"
278
- ],
279
- type: [
280
- "VerifiablePresentation"
281
- ],
282
- verifiableCredential: [
283
- vcObject
284
- ],
285
- holder: holderDid
286
- };
287
- const verifiablePresentationSP = await agent.createVerifiablePresentation({
288
- presentation: vpObject,
289
- proofFormat: "lds",
290
- keyRef: identifier.kmsKeyRef || identifier.kid
291
- });
292
- return {
293
- documentFormat,
294
- presentationPayload: verifiablePresentationSP
295
- };
296
- }
297
- case import_ssi_types.DocumentFormat.MSO_MDOC: {
298
- logger.warning("mso_mdoc format has basic support - production use requires proper mdoc VP token implementation");
299
- return {
300
- documentFormat,
301
- presentationPayload: originalCredential
302
- };
303
- }
304
- default: {
305
- const vcJwt = typeof originalCredential === "string" ? originalCredential : JSON.stringify(originalCredential);
306
- const vpPayload = {
307
- iss: holderDid,
308
- vp: {
309
- "@context": [
310
- "https://www.w3.org/2018/credentials/v1"
311
- ],
312
- type: [
313
- "VerifiablePresentation"
314
- ],
315
- holder: holderDid,
316
- verifiableCredential: [
317
- vcJwt
318
- ]
319
- },
320
- iat: Math.floor(Date.now() / 1e3 - CLOCK_SKEW),
321
- exp: Math.floor(Date.now() / 1e3 + 600 + CLOCK_SKEW)
322
- };
323
- const vpJwt = await agent.createVerifiablePresentation({
324
- presentation: vpPayload.vp,
325
- proofFormat: "jwt",
326
- keyRef: identifier.kmsKeyRef || identifier.kid
327
- });
328
- return {
329
- documentFormat,
330
- presentationPayload: vpJwt.proof && "jwt" in vpJwt.proof && vpJwt.proof.jwt || vpJwt
331
- };
332
- }
333
- }
303
+ const verifiablePresentationSP = await agent.createVerifiablePresentation({
304
+ presentation: vpObject,
305
+ proofFormat: "lds",
306
+ keyRef: identifier.kmsKeyRef || identifier.kid
307
+ });
308
+ return {
309
+ documentFormat: import_ssi_types.DocumentFormat.JSONLD,
310
+ presentationPayload: verifiablePresentationSP
311
+ };
334
312
  }
335
- __name(createLinkedVPPresentation, "createLinkedVPPresentation");
313
+ __name(createJsonLdPresentation, "createJsonLdPresentation");
314
+ async function createMdocPresentation(originalCredential) {
315
+ logger.warning("mso_mdoc format has basic support - production use requires proper mdoc VP token implementation");
316
+ return {
317
+ documentFormat: import_ssi_types.DocumentFormat.MSO_MDOC,
318
+ presentationPayload: originalCredential
319
+ };
320
+ }
321
+ __name(createMdocPresentation, "createMdocPresentation");
322
+ async function createJwtPresentation(holderDid, originalCredential, agent) {
323
+ const vcJwt = typeof originalCredential === "string" ? originalCredential : JSON.stringify(originalCredential);
324
+ const identifier = await agent.identifierManagedGet({
325
+ identifier: holderDid
326
+ });
327
+ const vpPayload = {
328
+ iss: holderDid,
329
+ vp: {
330
+ "@context": [
331
+ "https://www.w3.org/2018/credentials/v1"
332
+ ],
333
+ type: [
334
+ "VerifiablePresentation"
335
+ ],
336
+ holder: holderDid,
337
+ verifiableCredential: [
338
+ vcJwt
339
+ ]
340
+ },
341
+ iat: Math.floor(Date.now() / 1e3 - CLOCK_SKEW),
342
+ exp: Math.floor(Date.now() / 1e3 + 600 + CLOCK_SKEW)
343
+ };
344
+ const vpJwt = await agent.createVerifiablePresentation({
345
+ presentation: vpPayload.vp,
346
+ proofFormat: "jwt",
347
+ keyRef: identifier.kmsKeyRef || identifier.kid
348
+ });
349
+ return {
350
+ documentFormat: import_ssi_types.DocumentFormat.JWT,
351
+ presentationPayload: vpJwt.proof && "jwt" in vpJwt.proof && vpJwt.proof.jwt || vpJwt
352
+ };
353
+ }
354
+ __name(createJwtPresentation, "createJwtPresentation");
336
355
 
337
356
  // src/agent/LinkedVPManager.ts
338
357
  var linkedVPManagerMethods = [
@@ -431,7 +450,7 @@ var LinkedVPManager = class {
431
450
  filter
432
451
  ]
433
452
  });
434
- return credentials.filter((cred) => cred.linkedVpId !== void 0 && cred.linkedVpId !== null).flatMap((cred) => {
453
+ return credentials.flatMap((cred) => {
435
454
  const uniformDocument = JSON.parse(cred.uniformDocument);
436
455
  const holderDidForEntry = this.getHolderDid(uniformDocument);
437
456
  return holderDidForEntry && holderDidForEntry.startsWith("did:web") ? [
@@ -507,7 +526,10 @@ var LinkedVPManager = class {
507
526
  }
508
527
  buildLinkedVpId(linkedVpId, tenantId) {
509
528
  let finalLinkedVpId = linkedVpId || this.generateLinkedVpId();
510
- if (tenantId && tenantId !== "" && !finalLinkedVpId.includes("@")) {
529
+ if (linkedVpId && linkedVpId.includes("@")) {
530
+ throw new Error(`LinkedVP ID cannot contain '@' character as it is reserved for tenant separation`);
531
+ }
532
+ if (tenantId && tenantId !== "") {
511
533
  finalLinkedVpId = `${finalLinkedVpId}@${tenantId}`;
512
534
  }
513
535
  return finalLinkedVpId;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../plugin.schema.json","../src/agent/LinkedVPManager.ts","../src/services/LinkedVPService.ts","../src/types/ILinkedVPManager.ts"],"sourcesContent":["/**\n * @public\n */\nimport schema from '../plugin.schema.json'\nexport { schema }\nexport { LinkedVPManager, linkedVPManagerMethods } from './agent/LinkedVPManager'\nexport * from './types/ILinkedVPManager'\n","{\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 .filter((cred) => cred.linkedVpId !== undefined && cred.linkedVpId !== null)\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 // 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 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":["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","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","cred","flatMap","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"]}
1
+ {"version":3,"sources":["../src/index.ts","../plugin.schema.json","../src/agent/LinkedVPManager.ts","../src/services/LinkedVPService.ts","../src/types/ILinkedVPManager.ts"],"sourcesContent":["/**\n * @public\n */\nimport schema from '../plugin.schema.json'\nexport { schema }\nexport { LinkedVPManager, linkedVPManagerMethods } from './agent/LinkedVPManager'\nexport * from './types/ILinkedVPManager'\n","{\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":["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/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 = [
@@ -404,7 +423,7 @@ var LinkedVPManager = class {
404
423
  filter
405
424
  ]
406
425
  });
407
- return credentials.filter((cred) => cred.linkedVpId !== void 0 && cred.linkedVpId !== null).flatMap((cred) => {
426
+ return credentials.flatMap((cred) => {
408
427
  const uniformDocument = JSON.parse(cred.uniformDocument);
409
428
  const holderDidForEntry = this.getHolderDid(uniformDocument);
410
429
  return holderDidForEntry && holderDidForEntry.startsWith("did:web") ? [
@@ -480,7 +499,10 @@ var LinkedVPManager = class {
480
499
  }
481
500
  buildLinkedVpId(linkedVpId, tenantId) {
482
501
  let finalLinkedVpId = linkedVpId || this.generateLinkedVpId();
483
- 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 !== "") {
484
506
  finalLinkedVpId = `${finalLinkedVpId}@${tenantId}`;
485
507
  }
486
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 { 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 .filter((cred) => cred.linkedVpId !== undefined && cred.linkedVpId !== null)\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 // 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 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","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","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","cred","flatMap","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"]}
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.36.1-feature.SSISDK.82.and.SSISDK.70.37+4f1096f2",
3
+ "version": "0.36.1-next.39+f060eb6e",
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.36.1-feature.SSISDK.82.and.SSISDK.70.37+4f1096f2",
31
- "@sphereon/ssi-sdk.credential-store": "0.36.1-feature.SSISDK.82.and.SSISDK.70.37+4f1096f2",
32
- "@sphereon/ssi-sdk.credential-vcdm": "0.36.1-feature.SSISDK.82.and.SSISDK.70.37+4f1096f2",
33
- "@sphereon/ssi-sdk.data-store": "0.36.1-feature.SSISDK.82.and.SSISDK.70.37+4f1096f2",
34
- "@sphereon/ssi-sdk.data-store-types": "0.36.1-feature.SSISDK.82.and.SSISDK.70.37+4f1096f2",
35
- "@sphereon/ssi-sdk.sd-jwt": "0.36.1-feature.SSISDK.82.and.SSISDK.70.37+4f1096f2",
36
- "@sphereon/ssi-types": "0.36.1-feature.SSISDK.82.and.SSISDK.70.37+4f1096f2",
30
+ "@sphereon/ssi-sdk-ext.identifier-resolution": "0.36.1-next.39+f060eb6e",
31
+ "@sphereon/ssi-sdk.credential-store": "0.36.1-next.39+f060eb6e",
32
+ "@sphereon/ssi-sdk.credential-vcdm": "0.36.1-next.39+f060eb6e",
33
+ "@sphereon/ssi-sdk.data-store": "0.36.1-next.39+f060eb6e",
34
+ "@sphereon/ssi-sdk.data-store-types": "0.36.1-next.39+f060eb6e",
35
+ "@sphereon/ssi-sdk.sd-jwt": "0.36.1-next.39+f060eb6e",
36
+ "@sphereon/ssi-types": "0.36.1-next.39+f060eb6e",
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.36.1-feature.SSISDK.82.and.SSISDK.70.37+4f1096f2",
46
- "@sphereon/ssi-sdk-ext.kms-local": "0.36.1-feature.SSISDK.82.and.SSISDK.70.37+4f1096f2",
47
- "@sphereon/ssi-sdk.agent-config": "0.36.1-feature.SSISDK.82.and.SSISDK.70.37+4f1096f2",
48
- "@sphereon/ssi-sdk.credential-vcdm1-jwt-provider": "0.36.1-feature.SSISDK.82.and.SSISDK.70.37+4f1096f2",
45
+ "@sd-jwt/types": "^0.15.0",
46
+ "@sphereon/ssi-sdk-ext.key-manager": "0.36.1-next.39+f060eb6e",
47
+ "@sphereon/ssi-sdk-ext.kms-local": "0.36.1-next.39+f060eb6e",
48
+ "@sphereon/ssi-sdk.agent-config": "0.36.1-next.39+f060eb6e",
49
+ "@sphereon/ssi-sdk.credential-vcdm1-jwt-provider": "0.36.1-next.39+f060eb6e",
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": "4f1096f2d7ce22bdc20319a780386979393bc2ef"
80
+ "gitHead": "f060eb6e9930932f91cfbaa7535465d403a6c63c"
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',
@@ -121,7 +121,6 @@ export class LinkedVPManager implements IAgentPlugin {
121
121
  })
122
122
 
123
123
  return credentials
124
- .filter((cred) => cred.linkedVpId !== undefined && cred.linkedVpId !== null)
125
124
  .flatMap((cred) => {
126
125
  const uniformDocument = JSON.parse(cred.uniformDocument) as IVerifiableCredential
127
126
  const holderDidForEntry = this.getHolderDid(uniformDocument)
@@ -204,8 +203,13 @@ export class LinkedVPManager implements IAgentPlugin {
204
203
  private buildLinkedVpId(linkedVpId: string | undefined, tenantId: string | undefined) {
205
204
  let finalLinkedVpId = linkedVpId || this.generateLinkedVpId()
206
205
 
207
- // Append tenantId if provided and not already present
208
- if (tenantId && tenantId !== '' && !finalLinkedVpId.includes('@')) {
206
+ // Validate that user-provided ID doesn't contain @ char reserved for tenant id separator
207
+ if (linkedVpId && linkedVpId.includes('@')) {
208
+ throw new Error(`LinkedVP ID cannot contain '@' character as it is reserved for tenant separation`)
209
+ }
210
+
211
+ // Append tenantId if provided
212
+ if (tenantId && tenantId !== '') {
209
213
  finalLinkedVpId = `${finalLinkedVpId}@${tenantId}`
210
214
  }
211
215
  return finalLinkedVpId
@@ -14,6 +14,37 @@ import { LinkedVPPresentation, LOGGER_NAMESPACE, RequiredContext } from '../type
14
14
  const logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE)
15
15
  const CLOCK_SKEW = 120 // TODO make adjustable?
16
16
 
17
+ /**
18
+ * Creates a Verifiable Presentation for LinkedVP publishing
19
+ * Contains multiple credentials in a single JWT VP
20
+ * No nonce or audience since this is for publishing, not responding to verification
21
+ */
22
+ export async function createLinkedVPPresentation(
23
+ holderDid: string,
24
+ credential: UniqueDigitalCredential,
25
+ agent: RequiredContext['agent'],
26
+ ): Promise<LinkedVPPresentation> {
27
+ logger.debug(`Creating LinkedVP presentation for ${holderDid} of credential ${credential.id}`)
28
+
29
+ const originalCredential = extractOriginalCredential(credential)
30
+ const documentFormat = CredentialMapper.detectDocumentType(originalCredential)
31
+
32
+ switch (documentFormat) {
33
+ case DocumentFormat.SD_JWT_VC: {
34
+ return createSdJwtPresentation(originalCredential, agent)
35
+ }
36
+ case DocumentFormat.JSONLD: {
37
+ return createJsonLdPresentation(holderDid, originalCredential, agent)
38
+ }
39
+ case DocumentFormat.MSO_MDOC: {
40
+ return createMdocPresentation(originalCredential)
41
+ }
42
+ default: {
43
+ return createJwtPresentation(holderDid, originalCredential, agent)
44
+ }
45
+ }
46
+ }
47
+
17
48
  /**
18
49
  * Extracts the original credential from various wrapper types
19
50
  */
@@ -40,111 +71,123 @@ function extractOriginalCredential(
40
71
  }
41
72
 
42
73
  /**
43
- * Creates a Verifiable Presentation for LinkedVP publishing
44
- * Contains multiple credentials in a single JWT VP
45
- * No nonce or audience since this is for publishing, not responding to verification
74
+ * Creates an SD-JWT presentation with KB-JWT
46
75
  */
47
- export async function createLinkedVPPresentation(
76
+ async function createSdJwtPresentation(
77
+ originalCredential: OriginalVerifiableCredential,
78
+ agent: RequiredContext['agent'],
79
+ ): Promise<LinkedVPPresentation> {
80
+ // SD-JWT with KB-JWT
81
+ const decodedSdJwt = await CredentialMapper.decodeSdJwtVcAsync(
82
+ typeof originalCredential === 'string' ? originalCredential : (originalCredential as SdJwtDecodedVerifiableCredential).compactSdJwtVc,
83
+ defaultGenerateDigest,
84
+ )
85
+
86
+ const hashAlg = decodedSdJwt.signedPayload._sd_alg ?? 'sha-256'
87
+ const sdHash = calculateSdHash(decodedSdJwt.compactSdJwtVc, hashAlg, defaultGenerateDigest)
88
+ const kbJwtPayload: PartialSdJwtKbJwt['payload'] = {
89
+ iat: Math.floor(Date.now() / 1000 - CLOCK_SKEW),
90
+ sd_hash: sdHash,
91
+ }
92
+
93
+ const presentationResult = await agent.createSdJwtPresentation({
94
+ presentation: decodedSdJwt.compactSdJwtVc,
95
+ kb: {
96
+ payload: kbJwtPayload as any, // FIXME? (typescript seems impossible)
97
+ },
98
+ })
99
+
100
+ return {
101
+ documentFormat: DocumentFormat.SD_JWT_VC,
102
+ presentationPayload: presentationResult.presentation,
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Creates a JSON-LD presentation with proof
108
+ */
109
+ async function createJsonLdPresentation(
48
110
  holderDid: string,
49
- credential: UniqueDigitalCredential,
111
+ originalCredential: OriginalVerifiableCredential,
50
112
  agent: RequiredContext['agent'],
51
113
  ): Promise<LinkedVPPresentation> {
52
- logger.debug(`Creating LinkedVP presentation for ${holderDid} of credential ${credential.id}`)
114
+ // JSON-LD VC - create JSON-LD VP with challenge and domain in proof
115
+ const vcObject = typeof originalCredential === 'string' ? JSON.parse(originalCredential) : originalCredential
116
+
117
+ const vpObject = {
118
+ '@context': ['https://www.w3.org/2018/credentials/v1'],
119
+ type: ['VerifiablePresentation'],
120
+ verifiableCredential: [vcObject],
121
+ holder: holderDid,
122
+ }
53
123
 
54
124
  const identifier = await agent.identifierManagedGet({ identifier: holderDid })
55
- const originalCredential = extractOriginalCredential(credential)
56
- const documentFormat = CredentialMapper.detectDocumentType(originalCredential)
57
- switch (documentFormat) {
58
- case DocumentFormat.SD_JWT_VC: {
59
- // SD-JWT with KB-JWT
60
- const decodedSdJwt = await CredentialMapper.decodeSdJwtVcAsync(
61
- typeof originalCredential === 'string' ? originalCredential : (originalCredential as SdJwtDecodedVerifiableCredential).compactSdJwtVc,
62
- defaultGenerateDigest,
63
- )
64
-
65
- const hashAlg = decodedSdJwt.signedPayload._sd_alg ?? 'sha-256'
66
- const sdHash = calculateSdHash(decodedSdJwt.compactSdJwtVc, hashAlg, defaultGenerateDigest)
67
- const kbJwtPayload: PartialSdJwtKbJwt['payload'] = {
68
- iat: Math.floor(Date.now() / 1000 - CLOCK_SKEW),
69
- sd_hash: sdHash,
70
- }
71
-
72
- const presentationResult = await agent.createSdJwtPresentation({
73
- presentation: decodedSdJwt.compactSdJwtVc,
74
- kb: {
75
- payload: kbJwtPayload as any, // FIXME?
76
- },
77
- })
78
-
79
- return {
80
- documentFormat,
81
- presentationPayload: presentationResult.presentation,
82
- }
83
- }
84
- case DocumentFormat.JSONLD: {
85
- // JSON-LD VC - create JSON-LD VP with challenge and domain in proof
86
- const vcObject = typeof originalCredential === 'string' ? JSON.parse(originalCredential) : originalCredential
87
-
88
- const vpObject = {
89
- '@context': ['https://www.w3.org/2018/credentials/v1'],
90
- type: ['VerifiablePresentation'],
91
- verifiableCredential: [vcObject],
92
- holder: holderDid,
93
- }
94
-
95
- // Create JSON-LD VP with proof
96
- const verifiablePresentationSP = await agent.createVerifiablePresentation({
97
- presentation: vpObject,
98
- proofFormat: 'lds',
99
- keyRef: identifier.kmsKeyRef || identifier.kid,
100
- })
101
- return {
102
- documentFormat,
103
- presentationPayload: verifiablePresentationSP,
104
- }
105
- }
106
- case DocumentFormat.MSO_MDOC: {
107
- // ISO mdoc - create mdoc VP token
108
- // This is a placeholder implementation
109
- // Full implementation would require:
110
- // 1. Decode the mdoc using CredentialMapper or mdoc utilities
111
- // 2. Build proper mdoc VP token with session transcript
112
- // 3. Include nonce/audience in the session transcript
113
- logger.warning('mso_mdoc format has basic support - production use requires proper mdoc VP token implementation')
114
-
115
- return {
116
- documentFormat,
117
- presentationPayload: originalCredential,
118
- }
119
- }
120
- default: {
121
- // JWT VC - create JWT VP with nonce and aud in payload
122
- const vcJwt = typeof originalCredential === 'string' ? originalCredential : JSON.stringify(originalCredential)
123
-
124
- // Create VP JWT using agent method
125
- const vpPayload = {
126
- iss: holderDid,
127
- vp: {
128
- '@context': ['https://www.w3.org/2018/credentials/v1'],
129
- type: ['VerifiablePresentation'],
130
- holder: holderDid,
131
- verifiableCredential: [vcJwt],
132
- },
133
- iat: Math.floor(Date.now() / 1000 - CLOCK_SKEW),
134
- exp: Math.floor(Date.now() / 1000 + 600 + CLOCK_SKEW), // 10 minutes
135
- }
136
-
137
- // Use the agent's JWT creation capability
138
- const vpJwt = await agent.createVerifiablePresentation({
139
- presentation: vpPayload.vp,
140
- proofFormat: 'jwt',
141
- keyRef: identifier.kmsKeyRef || identifier.kid,
142
- })
143
-
144
- return {
145
- documentFormat,
146
- presentationPayload: (vpJwt.proof && 'jwt' in vpJwt.proof && vpJwt.proof.jwt) || vpJwt,
147
- }
148
- }
125
+
126
+ // Create JSON-LD VP with proof
127
+ const verifiablePresentationSP = await agent.createVerifiablePresentation({
128
+ presentation: vpObject,
129
+ proofFormat: 'lds',
130
+ keyRef: identifier.kmsKeyRef || identifier.kid,
131
+ })
132
+ return {
133
+ documentFormat: DocumentFormat.JSONLD,
134
+ presentationPayload: verifiablePresentationSP,
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Creates an ISO mdoc presentation (basic support)
140
+ */
141
+ async function createMdocPresentation(originalCredential: OriginalVerifiableCredential): Promise<LinkedVPPresentation> {
142
+ // ISO mdoc - create mdoc VP token
143
+ // This is a placeholder implementation
144
+ // Full implementation would require:
145
+ // 1. Decode the mdoc using CredentialMapper or mdoc utilities
146
+ // 2. Build proper mdoc VP token with session transcript
147
+ // 3. Include nonce/audience in the session transcript
148
+ logger.warning('mso_mdoc format has basic support - production use requires proper mdoc VP token implementation')
149
+
150
+ return {
151
+ documentFormat: DocumentFormat.MSO_MDOC,
152
+ presentationPayload: originalCredential,
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Creates a JWT presentation
158
+ */
159
+ async function createJwtPresentation(
160
+ holderDid: string,
161
+ originalCredential: OriginalVerifiableCredential,
162
+ agent: RequiredContext['agent'],
163
+ ): Promise<LinkedVPPresentation> {
164
+ // JWT VC - create JWT VP with nonce and aud in payload
165
+ const vcJwt = typeof originalCredential === 'string' ? originalCredential : JSON.stringify(originalCredential)
166
+
167
+ const identifier = await agent.identifierManagedGet({ identifier: holderDid })
168
+
169
+ // Create VP JWT using agent method
170
+ const vpPayload = {
171
+ iss: holderDid,
172
+ vp: {
173
+ '@context': ['https://www.w3.org/2018/credentials/v1'],
174
+ type: ['VerifiablePresentation'],
175
+ holder: holderDid,
176
+ verifiableCredential: [vcJwt],
177
+ },
178
+ iat: Math.floor(Date.now() / 1000 - CLOCK_SKEW),
179
+ exp: Math.floor(Date.now() / 1000 + 600 + CLOCK_SKEW), // 10 minutes
180
+ }
181
+
182
+ // Use the agent's JWT creation capability
183
+ const vpJwt = await agent.createVerifiablePresentation({
184
+ presentation: vpPayload.vp,
185
+ proofFormat: 'jwt',
186
+ keyRef: identifier.kmsKeyRef || identifier.kid,
187
+ })
188
+
189
+ return {
190
+ documentFormat: DocumentFormat.JWT,
191
+ presentationPayload: (vpJwt.proof && 'jwt' in vpJwt.proof && vpJwt.proof.jwt) || vpJwt,
149
192
  }
150
193
  }