@sphereon/ssi-sdk.linked-vp 0.34.1-feature.SSISDK.82.linkedVP.325 → 0.34.1-feature.SSISDK.82.linkedVP.327

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
@@ -217,6 +217,7 @@ var plugin_schema_default = {
217
217
  var import_typeorm = require("typeorm");
218
218
 
219
219
  // src/services/LinkedVPService.ts
220
+ var import_ssi_sdk = require("@sphereon/ssi-sdk.sd-jwt");
220
221
  var import_ssi_types = require("@sphereon/ssi-types");
221
222
 
222
223
  // src/types/ILinkedVPManager.ts
@@ -224,6 +225,7 @@ var LOGGER_NAMESPACE = "sphereon:linked-vp";
224
225
 
225
226
  // src/services/LinkedVPService.ts
226
227
  var logger = import_ssi_types.Loggers.DEFAULT.get(LOGGER_NAMESPACE);
228
+ var CLOCK_SKEW = 120;
227
229
  function extractOriginalCredential(credential) {
228
230
  if (typeof credential === "string") {
229
231
  return credential;
@@ -241,40 +243,94 @@ function extractOriginalCredential(credential) {
241
243
  return credential;
242
244
  }
243
245
  __name(extractOriginalCredential, "extractOriginalCredential");
244
- async function createLinkedVPPresentation(holderDid, credentials, agent) {
245
- if (credentials.length === 0) {
246
- return Promise.reject(Error("Cannot create LinkedVP presentation with zero credentials"));
247
- }
248
- logger.debug(`Creating LinkedVP presentation for ${holderDid} with ${credentials.length} credentials`);
246
+ async function createLinkedVPPresentation(holderDid, credential, agent) {
247
+ logger.debug(`Creating LinkedVP presentation for ${holderDid} of credential ${credential.id}`);
249
248
  const identifier = await agent.identifierManagedGet({
250
249
  identifier: holderDid
251
250
  });
252
- const verifiableCredentials = credentials.map((credential) => {
253
- const original = extractOriginalCredential(credential);
254
- return typeof original === "string" ? original : original;
255
- });
256
- const vpObject = {
257
- "@context": [
258
- "https://www.w3.org/2018/credentials/v1"
259
- ],
260
- type: [
261
- "VerifiablePresentation"
262
- ],
263
- holder: holderDid,
264
- verifiableCredential: verifiableCredentials
265
- };
266
- const result = await agent.createVerifiablePresentation({
267
- presentation: vpObject,
268
- proofFormat: "jwt",
269
- keyRef: identifier.kmsKeyRef || identifier.kid
270
- });
271
- if (typeof result === "string") {
272
- return result;
273
- }
274
- if (result.proof && "jws" in result.proof) {
275
- return result.proof.jws;
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
+ }
276
333
  }
277
- return Promise.reject(Error("Failed to create JWT VP - no JWT in result"));
278
334
  }
279
335
  __name(createLinkedVPPresentation, "createLinkedVPPresentation");
280
336
 
@@ -396,7 +452,10 @@ var LinkedVPManager = class {
396
452
  if (uniqueCredentials.length === 0) {
397
453
  return Promise.reject(Error(`No published credentials found for linkedVpId ${linkedVpId}`));
398
454
  }
399
- return createLinkedVPPresentation(holderDid, uniqueCredentials, context.agent);
455
+ if (uniqueCredentials.length > 1) {
456
+ return Promise.reject(Error(`Multiple credentials found for linkedVpId ${linkedVpId}`));
457
+ }
458
+ return createLinkedVPPresentation(holderDid, uniqueCredentials[0], context.agent);
400
459
  }
401
460
  getHolderDid(tenantId) {
402
461
  const holderDid = this.holderDids[tenantId ?? "default"];
@@ -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 { IAgentPlugin } from '@veramo/core'\nimport { IsNull, Not } from 'typeorm'\nimport { schema } from '../index'\nimport { createLinkedVPPresentation } from '../services/LinkedVPService'\nimport {\n GeneratePresentationArgs,\n GetServiceEntriesArgs,\n HasLinkedVPEntryArgs,\n ILinkedVPManager,\n LinkedVPEntry,\n LinkedVPPresentation,\n LinkedVPServiceEntry,\n PublishCredentialArgs,\n RequiredContext,\n UnpublishCredentialArgs,\n} from '../types'\n\n// Exposing the methods here for any REST implementation\nexport const linkedVPManagerMethods: Array<string> = [\n 'lvpPublishCredential',\n 'lvpUnpublishCredential',\n 'lvpHasEntry',\n 'lvpGetServiceEntries',\n 'lvpGeneratePresentation',\n]\n\n/**\n * {@inheritDoc ILinkedVPManager}\n */\nexport class LinkedVPManager implements IAgentPlugin {\n readonly schema = schema.ILinkedVPManager\n readonly methods: ILinkedVPManager = {\n lvpPublishCredential: this.lvpPublishCredential.bind(this),\n lvpUnpublishCredential: this.lvpUnpublishCredential.bind(this),\n lvpHasEntry: this.lvpHasEntry.bind(this),\n lvpGetServiceEntries: this.lvpGetServiceEntries.bind(this),\n lvpGeneratePresentation: this.lvpGeneratePresentation.bind(this),\n }\n\n private readonly holderDids: Record<string, string>\n\n constructor(options: { holderDids: Record<string, string> }) {\n this.holderDids = options.holderDids\n }\n\n private async lvpPublishCredential(args: PublishCredentialArgs, context: RequiredContext): Promise<LinkedVPEntry> {\n const { digitalCredentialId } = args\n\n const credential: DigitalCredential = await context.agent.crsGetCredential({ id: digitalCredentialId })\n\n if (credential.linkedVpId) {\n return Promise.reject(new Error(`Credential ${digitalCredentialId} is already published with linkedVpId ${credential.linkedVpId}`))\n }\n\n const linkedVpId = this.buildLinkedVpId(args.linkedVpId, credential.tenantId)\n\n await this.ensureLinkedVpIdUnique(linkedVpId, context, credential.tenantId)\n\n const publishedAt = new Date()\n await context.agent.crsUpdateCredential({\n id: digitalCredentialId,\n linkedVpId,\n linkedVpFrom: publishedAt,\n })\n\n return {\n id: credential.id,\n linkedVpId,\n tenantId: credential.tenantId,\n linkedVpFrom: publishedAt,\n createdAt: credential.createdAt,\n }\n }\n\n private async lvpUnpublishCredential(args: UnpublishCredentialArgs, context: RequiredContext): Promise<boolean> {\n const { linkedVpId } = args\n\n // Find credential by linkedVpId and tenantId\n const credentials = await context.agent.crsGetCredentials({\n filter: [{ linkedVpId }],\n })\n if (credentials.length === 0) {\n return Promise.reject(Error(`No credential found with linkedVpId ${linkedVpId}`))\n }\n\n const credential = credentials[0]\n await context.agent.crsUpdateCredential({\n id: credential.id,\n linkedVpId: undefined,\n linkedVpFrom: undefined,\n })\n\n return true\n }\n\n private async lvpHasEntry(args: HasLinkedVPEntryArgs, context: RequiredContext): Promise<boolean> {\n const { linkedVpId } = args\n\n try {\n const credentials = await context.agent.crsGetCredentials({\n filter: [{ linkedVpId }],\n })\n return credentials.length > 0\n } catch (error) {\n return false\n }\n }\n\n private async lvpGetServiceEntries(args: GetServiceEntriesArgs, context: RequiredContext): Promise<Array<LinkedVPServiceEntry>> {\n const { tenantId } = args\n\n // Get all published credentials (credentials with linkedVpId set)\n const filter: any = { linkedVpId: Not(IsNull()) }\n if (tenantId) {\n filter.tenantId = tenantId\n }\n\n const credentials = await context.agent.crsGetCredentials({\n filter: [filter],\n })\n\n return credentials\n .filter((cred) => cred.linkedVpId !== undefined && cred.linkedVpId !== null)\n .map((cred) => {\n const holderDidForEntry = this.getHolderDid(cred.tenantId)\n return this.credentialToServiceEntry(cred, holderDidForEntry)\n })\n }\n\n private async lvpGeneratePresentation(args: GeneratePresentationArgs, context: RequiredContext): Promise<LinkedVPPresentation> {\n const { linkedVpId } = args\n const tenantId = this.parseTenantFromLinkedVpId(linkedVpId)\n const holderDid = this.getHolderDid(tenantId)\n\n const uniqueCredentials = await context.agent.crsGetUniqueCredentials({\n filter: [\n {\n linkedVpId: args.linkedVpId,\n ...(tenantId && { tenantId }),\n },\n ],\n })\n if (uniqueCredentials.length === 0) {\n return Promise.reject(Error(`No published credentials found for linkedVpId ${linkedVpId}`))\n }\n\n // Generate the Verifiable Presentation with all published credentials\n return createLinkedVPPresentation(holderDid, uniqueCredentials, context.agent)\n }\n\n private getHolderDid(tenantId: string | undefined) {\n const holderDid = this.holderDids[tenantId ?? 'default']\n if (!holderDid) {\n throw Error(`No holder did supplied for tenant ${tenantId ?? 'default'}`)\n }\n return holderDid\n }\n\n private parseTenantFromLinkedVpId(linkedVpId: string): string | undefined {\n const idx = linkedVpId.lastIndexOf('@')\n return idx === -1 ? undefined : linkedVpId.substring(idx + 1)\n }\n\n private generateLinkedVpId(): string {\n return `lvp-${Date.now()}-${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 { Loggers, OriginalVerifiableCredential, WrappedVerifiableCredential } from '@sphereon/ssi-types'\nimport type { PresentationPayload } from '@veramo/core'\nimport { W3CVerifiableCredential } from '@veramo/core/src/types/vc-data-model'\nimport { LOGGER_NAMESPACE, RequiredContext } from '../types'\n\nconst logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE)\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 credentials: UniqueDigitalCredential[],\n agent: RequiredContext['agent'],\n): Promise<string | Record<string, any>> {\n if (credentials.length === 0) {\n return Promise.reject(Error('Cannot create LinkedVP presentation with zero credentials'))\n }\n\n logger.debug(`Creating LinkedVP presentation for ${holderDid} with ${credentials.length} credentials`)\n\n const identifier = await agent.identifierManagedGet({ identifier: holderDid })\n\n // Extract and prepare credentials\n const verifiableCredentials = credentials.map((credential) => {\n const original = extractOriginalCredential(credential)\n // Keep as-is if string (JWT), otherwise convert to object\n return typeof original === 'string' ? original : original\n })\n\n // Create VP structure\n const vpObject: PresentationPayload = {\n '@context': ['https://www.w3.org/2018/credentials/v1'],\n type: ['VerifiablePresentation'],\n holder: holderDid,\n verifiableCredential: verifiableCredentials as W3CVerifiableCredential[],\n }\n\n // Create and sign the VP as JWT\n const result = await agent.createVerifiablePresentation({\n presentation: vpObject,\n proofFormat: 'jwt',\n keyRef: identifier.kmsKeyRef || identifier.kid,\n })\n\n // Extract JWT from result\n if (typeof result === 'string') {\n return result\n }\n\n if (result.proof && 'jws' in result.proof) {\n return result.proof.jws\n }\n\n return Promise.reject(Error('Failed to create JWT VP - no JWT in result'))\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 { 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 = string | Record<string, any>\n\nexport interface ILinkedVPManager extends IPluginMethodMap {\n /**\n * Publish a credential as a LinkedVP by adding it to the holder's DID Document\n * @param args - Publication arguments including credential ID and scope configuration\n * @param context - Agent context\n */\n lvpPublishCredential(args: PublishCredentialArgs, context: RequiredContext): Promise<LinkedVPEntry>\n\n /**\n * Unpublish a credential by removing its LinkedVP entry from the DID Document\n * @param args - Unpublish arguments\n * @param context - Agent context\n */\n lvpUnpublishCredential(args: UnpublishCredentialArgs, context: RequiredContext): Promise<boolean>\n\n /**\n * Check if a LinkedVP entry exists by linkedVpId\n * @param args - Query arguments\n * @param context - Agent context\n */\n lvpHasEntry(args: HasLinkedVPEntryArgs, context: RequiredContext): Promise<boolean>\n\n /**\n * Get LinkedVP service entries for a DID to be added to a DID Document\n * This is useful when generating DID Documents with toDidDocument\n * @param args - Query arguments for the DID\n * @param context - Agent context\n */\n lvpGetServiceEntries(args: GetServiceEntriesArgs, context: RequiredContext): Promise<Array<LinkedVPServiceEntry>>\n\n /**\n * Generate and return a Verifiable Presentation for a published LinkedVP\n * This is the main endpoint handler for GET /linked-vp/{linkedVpId}\n * @param args - Generation arguments\n * @param context - Agent context\n */\n lvpGeneratePresentation(args: GeneratePresentationArgs, context: RequiredContext): Promise<LinkedVPPresentation>\n}\n\nexport type PublishCredentialArgs = {\n digitalCredentialId: string\n linkedVpId?: string // Optional: if not provided, will be auto-generated\n}\n\nexport type UnpublishCredentialArgs = {\n linkedVpId: string\n}\n\nexport type HasLinkedVPEntryArgs = {\n linkedVpId: string\n}\n\nexport type GetServiceEntriesArgs = {\n tenantId?: string\n}\n\nexport type GeneratePresentationArgs = {\n linkedVpId: string\n}\n\nexport type LinkedVPEntry = {\n id: string\n linkedVpId: string\n tenantId?: string\n linkedVpFrom?: Date\n createdAt: Date\n}\n\nexport type LinkedVPServiceEntry = {\n id: string\n type: 'LinkedVerifiablePresentation'\n serviceEndpoint: string\n}\n\nexport type RequiredContext = IAgentContext<IIdentifierResolution & ICredentialStore & IKeyManager & VcdmCredentialPlugin>\n"],"mappings":"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","names":["LOGGER_NAMESPACE","logger","Loggers","DEFAULT","get","LOGGER_NAMESPACE","extractOriginalCredential","credential","udc","originalVerifiableCredential","uniformVerifiableCredential","original","createLinkedVPPresentation","holderDid","credentials","agent","length","Promise","reject","Error","debug","identifier","identifierManagedGet","verifiableCredentials","map","vpObject","type","holder","verifiableCredential","result","createVerifiablePresentation","presentation","proofFormat","keyRef","kmsKeyRef","kid","proof","jws","linkedVPManagerMethods","LinkedVPManager","schema","ILinkedVPManager","methods","lvpPublishCredential","bind","lvpUnpublishCredential","lvpHasEntry","lvpGetServiceEntries","lvpGeneratePresentation","holderDids","options","args","context","digitalCredentialId","credential","agent","crsGetCredential","id","linkedVpId","Promise","reject","Error","buildLinkedVpId","tenantId","ensureLinkedVpIdUnique","publishedAt","Date","crsUpdateCredential","linkedVpFrom","createdAt","credentials","crsGetCredentials","filter","length","undefined","error","Not","IsNull","cred","map","holderDidForEntry","getHolderDid","credentialToServiceEntry","parseTenantFromLinkedVpId","holderDid","uniqueCredentials","crsGetUniqueCredentials","createLinkedVPPresentation","idx","lastIndexOf","substring","generateLinkedVpId","now","Math","random","toString","finalLinkedVpId","includes","getBaseUrlFromDid","startsWith","withoutPrefix","replace","parts","split","domain","shift","path","join","buildServiceEndpoint","baseUrl","cleanBaseUrl","endsWith","slice","type","serviceEndpoint"]}
1
+ {"version":3,"sources":["../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 { IAgentPlugin } from '@veramo/core'\nimport { IsNull, Not } from 'typeorm'\nimport { schema } from '../index'\nimport { createLinkedVPPresentation } from '../services/LinkedVPService'\nimport {\n GeneratePresentationArgs,\n GetServiceEntriesArgs,\n HasLinkedVPEntryArgs,\n ILinkedVPManager,\n LinkedVPEntry,\n LinkedVPPresentation,\n LinkedVPServiceEntry,\n PublishCredentialArgs,\n RequiredContext,\n UnpublishCredentialArgs,\n} from '../types'\n\n// Exposing the methods here for any REST implementation\nexport const linkedVPManagerMethods: Array<string> = [\n 'lvpPublishCredential',\n 'lvpUnpublishCredential',\n 'lvpHasEntry',\n 'lvpGetServiceEntries',\n 'lvpGeneratePresentation',\n]\n\n/**\n * {@inheritDoc ILinkedVPManager}\n */\nexport class LinkedVPManager implements IAgentPlugin {\n readonly schema = schema.ILinkedVPManager\n readonly methods: ILinkedVPManager = {\n lvpPublishCredential: this.lvpPublishCredential.bind(this),\n lvpUnpublishCredential: this.lvpUnpublishCredential.bind(this),\n lvpHasEntry: this.lvpHasEntry.bind(this),\n lvpGetServiceEntries: this.lvpGetServiceEntries.bind(this),\n lvpGeneratePresentation: this.lvpGeneratePresentation.bind(this),\n }\n\n private readonly holderDids: Record<string, string>\n\n constructor(options: { holderDids: Record<string, string> }) {\n this.holderDids = options.holderDids\n }\n\n private async lvpPublishCredential(args: PublishCredentialArgs, context: RequiredContext): Promise<LinkedVPEntry> {\n const { digitalCredentialId } = args\n\n const credential: DigitalCredential = await context.agent.crsGetCredential({ id: digitalCredentialId })\n\n if (credential.linkedVpId) {\n return Promise.reject(new Error(`Credential ${digitalCredentialId} is already published with linkedVpId ${credential.linkedVpId}`))\n }\n\n const linkedVpId = this.buildLinkedVpId(args.linkedVpId, credential.tenantId)\n\n await this.ensureLinkedVpIdUnique(linkedVpId, context, credential.tenantId)\n\n const publishedAt = new Date()\n await context.agent.crsUpdateCredential({\n id: digitalCredentialId,\n linkedVpId,\n linkedVpFrom: publishedAt,\n })\n\n return {\n id: credential.id,\n linkedVpId,\n tenantId: credential.tenantId,\n linkedVpFrom: publishedAt,\n createdAt: credential.createdAt,\n }\n }\n\n private async lvpUnpublishCredential(args: UnpublishCredentialArgs, context: RequiredContext): Promise<boolean> {\n const { linkedVpId } = args\n\n // Find credential by linkedVpId and tenantId\n const credentials = await context.agent.crsGetCredentials({\n filter: [{ linkedVpId }],\n })\n if (credentials.length === 0) {\n return Promise.reject(Error(`No credential found with linkedVpId ${linkedVpId}`))\n }\n\n const credential = credentials[0]\n await context.agent.crsUpdateCredential({\n id: credential.id,\n linkedVpId: undefined,\n linkedVpFrom: undefined,\n })\n\n return true\n }\n\n private async lvpHasEntry(args: HasLinkedVPEntryArgs, context: RequiredContext): Promise<boolean> {\n const { linkedVpId } = args\n\n try {\n const credentials = await context.agent.crsGetCredentials({\n filter: [{ linkedVpId }],\n })\n return credentials.length > 0\n } catch (error) {\n return false\n }\n }\n\n private async lvpGetServiceEntries(args: GetServiceEntriesArgs, context: RequiredContext): Promise<Array<LinkedVPServiceEntry>> {\n const { tenantId } = args\n\n // Get all published credentials (credentials with linkedVpId set)\n const filter: any = { linkedVpId: Not(IsNull()) }\n if (tenantId) {\n filter.tenantId = tenantId\n }\n\n const credentials = await context.agent.crsGetCredentials({\n filter: [filter],\n })\n\n return credentials\n .filter((cred) => cred.linkedVpId !== undefined && cred.linkedVpId !== null)\n .map((cred) => {\n const holderDidForEntry = this.getHolderDid(cred.tenantId)\n return this.credentialToServiceEntry(cred, holderDidForEntry)\n })\n }\n\n private async lvpGeneratePresentation(args: GeneratePresentationArgs, context: RequiredContext): Promise<LinkedVPPresentation> {\n const { linkedVpId } = args\n const tenantId = this.parseTenantFromLinkedVpId(linkedVpId)\n const holderDid = this.getHolderDid(tenantId)\n\n const uniqueCredentials = await context.agent.crsGetUniqueCredentials({\n filter: [\n {\n linkedVpId: args.linkedVpId,\n ...(tenantId && { tenantId }),\n },\n ],\n })\n if (uniqueCredentials.length === 0) {\n return Promise.reject(Error(`No published credentials found for linkedVpId ${linkedVpId}`))\n }\n if (uniqueCredentials.length > 1) {\n return Promise.reject(Error(`Multiple credentials found for linkedVpId ${linkedVpId}`))\n }\n\n // Generate the Verifiable Presentation with all published credentials\n return createLinkedVPPresentation(holderDid, uniqueCredentials[0], context.agent)\n }\n\n private getHolderDid(tenantId: string | undefined) {\n const holderDid = this.holderDids[tenantId ?? 'default']\n if (!holderDid) {\n throw Error(`No holder did supplied for tenant ${tenantId ?? 'default'}`)\n }\n return holderDid\n }\n\n private parseTenantFromLinkedVpId(linkedVpId: string): string | undefined {\n const idx = linkedVpId.lastIndexOf('@')\n return idx === -1 ? undefined : linkedVpId.substring(idx + 1)\n }\n\n private generateLinkedVpId(): string {\n return `lvp-${Date.now()}-${Math.random().toString(36).substring(2, 15)}`\n }\n\n private async ensureLinkedVpIdUnique(linkedVpId: string, context: RequiredContext, tenantId?: string): Promise<void> {\n const credentials = await context.agent.crsGetCredentials({\n filter: [{ linkedVpId, ...(tenantId && { tenantId }) }],\n })\n\n if (credentials.length > 0) {\n throw new Error(`LinkedVP ID ${linkedVpId} already exists${tenantId ? ` for tenant ${tenantId}` : ''}`)\n }\n }\n\n private buildLinkedVpId(linkedVpId: string | undefined, tenantId: string | undefined) {\n let finalLinkedVpId = linkedVpId || this.generateLinkedVpId()\n\n // Append tenantId if provided and not already present\n if (tenantId && tenantId !== '' && !finalLinkedVpId.includes('@')) {\n finalLinkedVpId = `${finalLinkedVpId}@${tenantId}`\n }\n return finalLinkedVpId\n }\n\n private getBaseUrlFromDid(holderDid: string): string {\n if (!holderDid.startsWith('did:web:')) {\n throw new Error(`Invalid DID: ${holderDid}, must be did:web`)\n }\n\n const withoutPrefix = holderDid.replace('did:web:', '') // example.com:tenants:tenant1\n const parts = withoutPrefix.split(':')\n const domain = parts.shift()! // example.com\n const path = parts.join('/') // tenants/tenant1\n\n return path\n ? `https://${domain}/${path}` // https://example.com/tenants/tenant1\n : `https://${domain}` // https://example.com\n }\n\n private buildServiceEndpoint(holderDid: string, linkedVpId: string): string {\n const baseUrl = this.getBaseUrlFromDid(holderDid)\n const cleanBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl\n return `${cleanBaseUrl}/linked-vp/${linkedVpId}`\n }\n\n private credentialToServiceEntry(credential: DigitalCredential, holderDid: string): LinkedVPServiceEntry {\n if (!credential.linkedVpId) {\n throw new Error(`Credential ${credential.id} does not have a linkedVpId`)\n }\n\n return {\n id: `${holderDid}#${credential.linkedVpId}`,\n type: 'LinkedVerifiablePresentation',\n serviceEndpoint: this.buildServiceEndpoint(holderDid, credential.linkedVpId),\n }\n }\n}\n","import { UniqueDigitalCredential } from '@sphereon/ssi-sdk.credential-store'\nimport { calculateSdHash, defaultGenerateDigest, PartialSdJwtKbJwt } from '@sphereon/ssi-sdk.sd-jwt'\n\nimport {\n CredentialMapper,\n DocumentFormat,\n Loggers,\n OriginalVerifiableCredential,\n SdJwtDecodedVerifiableCredential,\n WrappedVerifiableCredential,\n} from '@sphereon/ssi-types'\nimport { LinkedVPPresentation, LOGGER_NAMESPACE, RequiredContext } from '../types'\n\nconst logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE)\nconst CLOCK_SKEW = 120 // TODO make adjustable?\n\n/**\n * Extracts the original credential from various wrapper types\n */\nfunction extractOriginalCredential(\n credential: UniqueDigitalCredential | WrappedVerifiableCredential | OriginalVerifiableCredential,\n): OriginalVerifiableCredential {\n if (typeof credential === 'string') {\n return credential\n }\n\n if ('digitalCredential' in credential) {\n const udc = credential as UniqueDigitalCredential\n if (udc.originalVerifiableCredential) {\n return udc.originalVerifiableCredential\n }\n return udc.uniformVerifiableCredential as OriginalVerifiableCredential\n }\n\n if ('original' in credential) {\n return credential.original\n }\n\n return credential as OriginalVerifiableCredential\n}\n\n/**\n * Creates a Verifiable Presentation for LinkedVP publishing\n * Contains multiple credentials in a single JWT VP\n * No nonce or audience since this is for publishing, not responding to verification\n */\nexport async function createLinkedVPPresentation(\n holderDid: string,\n credential: UniqueDigitalCredential,\n agent: RequiredContext['agent'],\n): Promise<LinkedVPPresentation> {\n logger.debug(`Creating LinkedVP presentation for ${holderDid} of credential ${credential.id}`)\n\n const identifier = await agent.identifierManagedGet({ identifier: holderDid })\n const originalCredential = extractOriginalCredential(credential)\n const documentFormat = CredentialMapper.detectDocumentType(originalCredential)\n switch (documentFormat) {\n case DocumentFormat.SD_JWT_VC: {\n // SD-JWT with KB-JWT\n const decodedSdJwt = await CredentialMapper.decodeSdJwtVcAsync(\n typeof originalCredential === 'string' ? originalCredential : (originalCredential as SdJwtDecodedVerifiableCredential).compactSdJwtVc,\n defaultGenerateDigest,\n )\n\n const hashAlg = decodedSdJwt.signedPayload._sd_alg ?? 'sha-256'\n const sdHash = calculateSdHash(decodedSdJwt.compactSdJwtVc, hashAlg, defaultGenerateDigest)\n const kbJwtPayload: PartialSdJwtKbJwt['payload'] = {\n iat: Math.floor(Date.now() / 1000 - CLOCK_SKEW),\n sd_hash: sdHash,\n }\n\n const presentationResult = await agent.createSdJwtPresentation({\n presentation: decodedSdJwt.compactSdJwtVc,\n kb: {\n payload: kbJwtPayload as any, // FIXME?\n },\n })\n\n return {\n documentFormat,\n presentationPayload: presentationResult.presentation,\n }\n }\n case DocumentFormat.JSONLD: {\n // JSON-LD VC - create JSON-LD VP with challenge and domain in proof\n const vcObject = typeof originalCredential === 'string' ? JSON.parse(originalCredential) : originalCredential\n\n const vpObject = {\n '@context': ['https://www.w3.org/2018/credentials/v1'],\n type: ['VerifiablePresentation'],\n verifiableCredential: [vcObject],\n holder: holderDid,\n }\n\n // Create JSON-LD VP with proof\n const verifiablePresentationSP = await agent.createVerifiablePresentation({\n presentation: vpObject,\n proofFormat: 'lds',\n keyRef: identifier.kmsKeyRef || identifier.kid,\n })\n return {\n documentFormat,\n presentationPayload: verifiablePresentationSP,\n }\n }\n case DocumentFormat.MSO_MDOC: {\n // ISO mdoc - create mdoc VP token\n // This is a placeholder implementation\n // Full implementation would require:\n // 1. Decode the mdoc using CredentialMapper or mdoc utilities\n // 2. Build proper mdoc VP token with session transcript\n // 3. Include nonce/audience in the session transcript\n logger.warning('mso_mdoc format has basic support - production use requires proper mdoc VP token implementation')\n\n return {\n documentFormat,\n presentationPayload: originalCredential,\n }\n }\n default: {\n // JWT VC - create JWT VP with nonce and aud in payload\n const vcJwt = typeof originalCredential === 'string' ? originalCredential : JSON.stringify(originalCredential)\n\n // Create VP JWT using agent method\n const vpPayload = {\n iss: holderDid,\n vp: {\n '@context': ['https://www.w3.org/2018/credentials/v1'],\n type: ['VerifiablePresentation'],\n holder: holderDid,\n verifiableCredential: [vcJwt],\n },\n iat: Math.floor(Date.now() / 1000 - CLOCK_SKEW),\n exp: Math.floor(Date.now() / 1000 + 600 + CLOCK_SKEW), // 10 minutes\n }\n\n // Use the agent's JWT creation capability\n const vpJwt = await agent.createVerifiablePresentation({\n presentation: vpPayload.vp,\n proofFormat: 'jwt',\n keyRef: identifier.kmsKeyRef || identifier.kid,\n })\n\n return {\n documentFormat,\n presentationPayload: (vpJwt.proof && 'jwt' in vpJwt.proof && vpJwt.proof.jwt) || vpJwt,\n }\n }\n }\n}\n","import { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'\nimport { ICredentialStore } from '@sphereon/ssi-sdk.credential-store'\nimport { VcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'\nimport { ISDJwtPlugin } from '@sphereon/ssi-sdk.sd-jwt'\nimport { DocumentFormat } from '@sphereon/ssi-types'\nimport { IAgentContext, IPluginMethodMap } from '@veramo/core'\nimport { IKeyManager } from '@veramo/core/src/types/IKeyManager'\n\nexport const LOGGER_NAMESPACE = 'sphereon:linked-vp'\n\nexport type LinkedVPPresentation = {\n documentFormat: DocumentFormat\n presentationPayload: string | Record<string, any>\n}\n\nexport interface ILinkedVPManager extends IPluginMethodMap {\n /**\n * Publish a credential as a LinkedVP by adding it to the holder's DID Document\n * @param args - Publication arguments including credential ID and scope configuration\n * @param context - Agent context\n */\n lvpPublishCredential(args: PublishCredentialArgs, context: RequiredContext): Promise<LinkedVPEntry>\n\n /**\n * Unpublish a credential by removing its LinkedVP entry from the DID Document\n * @param args - Unpublish arguments\n * @param context - Agent context\n */\n lvpUnpublishCredential(args: UnpublishCredentialArgs, context: RequiredContext): Promise<boolean>\n\n /**\n * Check if a LinkedVP entry exists by linkedVpId\n * @param args - Query arguments\n * @param context - Agent context\n */\n lvpHasEntry(args: HasLinkedVPEntryArgs, context: RequiredContext): Promise<boolean>\n\n /**\n * Get LinkedVP service entries for a DID to be added to a DID Document\n * This is useful when generating DID Documents with toDidDocument\n * @param args - Query arguments for the DID\n * @param context - Agent context\n */\n lvpGetServiceEntries(args: GetServiceEntriesArgs, context: RequiredContext): Promise<Array<LinkedVPServiceEntry>>\n\n /**\n * Generate and return a Verifiable Presentation for a published LinkedVP\n * This is the main endpoint handler for GET /linked-vp/{linkedVpId}\n * @param args - Generation arguments\n * @param context - Agent context\n */\n lvpGeneratePresentation(args: GeneratePresentationArgs, context: RequiredContext): Promise<LinkedVPPresentation>\n}\n\nexport type PublishCredentialArgs = {\n digitalCredentialId: string\n linkedVpId?: string // Optional: if not provided, will be auto-generated\n}\n\nexport type UnpublishCredentialArgs = {\n linkedVpId: string\n}\n\nexport type HasLinkedVPEntryArgs = {\n linkedVpId: string\n}\n\nexport type GetServiceEntriesArgs = {\n tenantId?: string\n}\n\nexport type GeneratePresentationArgs = {\n linkedVpId: string\n}\n\nexport type LinkedVPEntry = {\n id: string\n linkedVpId: string\n tenantId?: string\n linkedVpFrom?: Date\n createdAt: Date\n}\n\nexport type LinkedVPServiceEntry = {\n id: string\n type: 'LinkedVerifiablePresentation'\n serviceEndpoint: string\n}\n\nexport type RequiredContext = IAgentContext<IIdentifierResolution & ICredentialStore & IKeyManager & VcdmCredentialPlugin & ISDJwtPlugin>\n"],"mappings":"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","names":["LOGGER_NAMESPACE","logger","Loggers","DEFAULT","get","LOGGER_NAMESPACE","CLOCK_SKEW","extractOriginalCredential","credential","udc","originalVerifiableCredential","uniformVerifiableCredential","original","createLinkedVPPresentation","holderDid","agent","debug","id","identifier","identifierManagedGet","originalCredential","documentFormat","CredentialMapper","detectDocumentType","DocumentFormat","SD_JWT_VC","decodedSdJwt","decodeSdJwtVcAsync","compactSdJwtVc","defaultGenerateDigest","hashAlg","signedPayload","_sd_alg","sdHash","calculateSdHash","kbJwtPayload","iat","Math","floor","Date","now","sd_hash","presentationResult","createSdJwtPresentation","presentation","kb","payload","presentationPayload","JSONLD","vcObject","JSON","parse","vpObject","type","verifiableCredential","holder","verifiablePresentationSP","createVerifiablePresentation","proofFormat","keyRef","kmsKeyRef","kid","MSO_MDOC","warning","vcJwt","stringify","vpPayload","iss","vp","exp","vpJwt","proof","jwt","linkedVPManagerMethods","LinkedVPManager","schema","ILinkedVPManager","methods","lvpPublishCredential","bind","lvpUnpublishCredential","lvpHasEntry","lvpGetServiceEntries","lvpGeneratePresentation","holderDids","options","args","context","digitalCredentialId","credential","agent","crsGetCredential","id","linkedVpId","Promise","reject","Error","buildLinkedVpId","tenantId","ensureLinkedVpIdUnique","publishedAt","Date","crsUpdateCredential","linkedVpFrom","createdAt","credentials","crsGetCredentials","filter","length","undefined","error","Not","IsNull","cred","map","holderDidForEntry","getHolderDid","credentialToServiceEntry","parseTenantFromLinkedVpId","holderDid","uniqueCredentials","crsGetUniqueCredentials","createLinkedVPPresentation","idx","lastIndexOf","substring","generateLinkedVpId","now","Math","random","toString","finalLinkedVpId","includes","getBaseUrlFromDid","startsWith","withoutPrefix","replace","parts","split","domain","shift","path","join","buildServiceEndpoint","baseUrl","cleanBaseUrl","endsWith","slice","type","serviceEndpoint"]}
package/dist/index.d.cts CHANGED
@@ -2,6 +2,8 @@ import { IPluginMethodMap, IAgentContext, IAgentPlugin } from '@veramo/core';
2
2
  import { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution';
3
3
  import { ICredentialStore } from '@sphereon/ssi-sdk.credential-store';
4
4
  import { VcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm';
5
+ import { ISDJwtPlugin } from '@sphereon/ssi-sdk.sd-jwt';
6
+ import { DocumentFormat } from '@sphereon/ssi-types';
5
7
  import { IKeyManager } from '@veramo/core/src/types/IKeyManager';
6
8
 
7
9
  var ILinkedVPManager$1 = {
@@ -190,7 +192,10 @@ var plugin_schema = {
190
192
  };
191
193
 
192
194
  declare const LOGGER_NAMESPACE = "sphereon:linked-vp";
193
- type LinkedVPPresentation = string | Record<string, any>;
195
+ type LinkedVPPresentation = {
196
+ documentFormat: DocumentFormat;
197
+ presentationPayload: string | Record<string, any>;
198
+ };
194
199
  interface ILinkedVPManager extends IPluginMethodMap {
195
200
  /**
196
201
  * Publish a credential as a LinkedVP by adding it to the holder's DID Document
@@ -253,7 +258,7 @@ type LinkedVPServiceEntry = {
253
258
  type: 'LinkedVerifiablePresentation';
254
259
  serviceEndpoint: string;
255
260
  };
256
- type RequiredContext = IAgentContext<IIdentifierResolution & ICredentialStore & IKeyManager & VcdmCredentialPlugin>;
261
+ type RequiredContext = IAgentContext<IIdentifierResolution & ICredentialStore & IKeyManager & VcdmCredentialPlugin & ISDJwtPlugin>;
257
262
 
258
263
  declare const linkedVPManagerMethods: Array<string>;
259
264
  /**
package/dist/index.d.ts CHANGED
@@ -2,6 +2,8 @@ import { IPluginMethodMap, IAgentContext, IAgentPlugin } from '@veramo/core';
2
2
  import { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution';
3
3
  import { ICredentialStore } from '@sphereon/ssi-sdk.credential-store';
4
4
  import { VcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm';
5
+ import { ISDJwtPlugin } from '@sphereon/ssi-sdk.sd-jwt';
6
+ import { DocumentFormat } from '@sphereon/ssi-types';
5
7
  import { IKeyManager } from '@veramo/core/src/types/IKeyManager';
6
8
 
7
9
  var ILinkedVPManager$1 = {
@@ -190,7 +192,10 @@ var plugin_schema = {
190
192
  };
191
193
 
192
194
  declare const LOGGER_NAMESPACE = "sphereon:linked-vp";
193
- type LinkedVPPresentation = string | Record<string, any>;
195
+ type LinkedVPPresentation = {
196
+ documentFormat: DocumentFormat;
197
+ presentationPayload: string | Record<string, any>;
198
+ };
194
199
  interface ILinkedVPManager extends IPluginMethodMap {
195
200
  /**
196
201
  * Publish a credential as a LinkedVP by adding it to the holder's DID Document
@@ -253,7 +258,7 @@ type LinkedVPServiceEntry = {
253
258
  type: 'LinkedVerifiablePresentation';
254
259
  serviceEndpoint: string;
255
260
  };
256
- type RequiredContext = IAgentContext<IIdentifierResolution & ICredentialStore & IKeyManager & VcdmCredentialPlugin>;
261
+ type RequiredContext = IAgentContext<IIdentifierResolution & ICredentialStore & IKeyManager & VcdmCredentialPlugin & ISDJwtPlugin>;
257
262
 
258
263
  declare const linkedVPManagerMethods: Array<string>;
259
264
  /**
package/dist/index.js CHANGED
@@ -190,13 +190,15 @@ var plugin_schema_default = {
190
190
  import { IsNull, Not } from "typeorm";
191
191
 
192
192
  // src/services/LinkedVPService.ts
193
- import { Loggers } from "@sphereon/ssi-types";
193
+ import { calculateSdHash, defaultGenerateDigest } from "@sphereon/ssi-sdk.sd-jwt";
194
+ import { CredentialMapper, DocumentFormat, Loggers } from "@sphereon/ssi-types";
194
195
 
195
196
  // src/types/ILinkedVPManager.ts
196
197
  var LOGGER_NAMESPACE = "sphereon:linked-vp";
197
198
 
198
199
  // src/services/LinkedVPService.ts
199
200
  var logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE);
201
+ var CLOCK_SKEW = 120;
200
202
  function extractOriginalCredential(credential) {
201
203
  if (typeof credential === "string") {
202
204
  return credential;
@@ -214,40 +216,94 @@ function extractOriginalCredential(credential) {
214
216
  return credential;
215
217
  }
216
218
  __name(extractOriginalCredential, "extractOriginalCredential");
217
- async function createLinkedVPPresentation(holderDid, credentials, agent) {
218
- if (credentials.length === 0) {
219
- return Promise.reject(Error("Cannot create LinkedVP presentation with zero credentials"));
220
- }
221
- logger.debug(`Creating LinkedVP presentation for ${holderDid} with ${credentials.length} credentials`);
219
+ async function createLinkedVPPresentation(holderDid, credential, agent) {
220
+ logger.debug(`Creating LinkedVP presentation for ${holderDid} of credential ${credential.id}`);
222
221
  const identifier = await agent.identifierManagedGet({
223
222
  identifier: holderDid
224
223
  });
225
- const verifiableCredentials = credentials.map((credential) => {
226
- const original = extractOriginalCredential(credential);
227
- return typeof original === "string" ? original : original;
228
- });
229
- const vpObject = {
230
- "@context": [
231
- "https://www.w3.org/2018/credentials/v1"
232
- ],
233
- type: [
234
- "VerifiablePresentation"
235
- ],
236
- holder: holderDid,
237
- verifiableCredential: verifiableCredentials
238
- };
239
- const result = await agent.createVerifiablePresentation({
240
- presentation: vpObject,
241
- proofFormat: "jwt",
242
- keyRef: identifier.kmsKeyRef || identifier.kid
243
- });
244
- if (typeof result === "string") {
245
- return result;
246
- }
247
- if (result.proof && "jws" in result.proof) {
248
- return result.proof.jws;
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
+ }
249
306
  }
250
- return Promise.reject(Error("Failed to create JWT VP - no JWT in result"));
251
307
  }
252
308
  __name(createLinkedVPPresentation, "createLinkedVPPresentation");
253
309
 
@@ -369,7 +425,10 @@ var LinkedVPManager = class {
369
425
  if (uniqueCredentials.length === 0) {
370
426
  return Promise.reject(Error(`No published credentials found for linkedVpId ${linkedVpId}`));
371
427
  }
372
- return createLinkedVPPresentation(holderDid, uniqueCredentials, context.agent);
428
+ if (uniqueCredentials.length > 1) {
429
+ return Promise.reject(Error(`Multiple credentials found for linkedVpId ${linkedVpId}`));
430
+ }
431
+ return createLinkedVPPresentation(holderDid, uniqueCredentials[0], context.agent);
373
432
  }
374
433
  getHolderDid(tenantId) {
375
434
  const holderDid = this.holderDids[tenantId ?? "default"];
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../plugin.schema.json","../src/agent/LinkedVPManager.ts","../src/services/LinkedVPService.ts","../src/types/ILinkedVPManager.ts"],"sourcesContent":["{\n \"ILinkedVPManager\": {\n \"components\": {\n \"schemas\": {\n \"GeneratePresentationArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"linkedVpId\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"linkedVpId\"\n ],\n \"additionalProperties\": false\n },\n \"LinkedVPPresentation\": {\n \"anyOf\": [\n {\n \"type\": \"string\"\n },\n {\n \"$ref\": \"#/components/schemas/Record<string,any>\"\n }\n ]\n },\n \"Record<string,any>\": {\n \"type\": \"object\"\n },\n \"GetServiceEntriesArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"tenantId\": {\n \"type\": \"string\"\n }\n },\n \"additionalProperties\": false\n },\n \"LinkedVPServiceEntry\": {\n \"type\": \"object\",\n \"properties\": {\n \"id\": {\n \"type\": \"string\"\n },\n \"type\": {\n \"type\": \"string\",\n \"const\": \"LinkedVerifiablePresentation\"\n },\n \"serviceEndpoint\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"id\",\n \"type\",\n \"serviceEndpoint\"\n ],\n \"additionalProperties\": false\n },\n \"HasLinkedVPEntryArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"linkedVpId\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"linkedVpId\"\n ],\n \"additionalProperties\": false\n },\n \"PublishCredentialArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"digitalCredentialId\": {\n \"type\": \"string\"\n },\n \"linkedVpId\": {\n \"type\": \"string\"\n },\n \"tenantId\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"digitalCredentialId\"\n ],\n \"additionalProperties\": false\n },\n \"LinkedVPEntry\": {\n \"type\": \"object\",\n \"properties\": {\n \"id\": {\n \"type\": \"string\"\n },\n \"linkedVpId\": {\n \"type\": \"string\"\n },\n \"tenantId\": {\n \"type\": \"string\"\n },\n \"linkedVpFrom\": {\n \"type\": \"string\",\n \"format\": \"date-time\"\n },\n \"createdAt\": {\n \"type\": \"string\",\n \"format\": \"date-time\"\n }\n },\n \"required\": [\n \"id\",\n \"linkedVpId\",\n \"createdAt\"\n ],\n \"additionalProperties\": false\n },\n \"UnpublishCredentialArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"linkedVpId\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"linkedVpId\"\n ],\n \"additionalProperties\": false\n }\n },\n \"methods\": {\n \"lvpGeneratePresentation\": {\n \"description\": \"Generate and return a Verifiable Presentation for a published LinkedVP This is the main endpoint handler for GET /linked-vp/\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/GeneratePresentationArgs\"\n },\n \"returnType\": {\n \"$ref\": \"#/components/schemas/LinkedVPPresentation\"\n }\n },\n \"lvpGetServiceEntries\": {\n \"description\": \"Get LinkedVP service entries for a DID to be added to a DID Document This is useful when generating DID Documents with toDidDocument\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/GetServiceEntriesArgs\"\n },\n \"returnType\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/components/schemas/LinkedVPServiceEntry\"\n }\n }\n },\n \"lvpHasEntry\": {\n \"description\": \"Check if a LinkedVP entry exists by linkedVpId\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/HasLinkedVPEntryArgs\"\n },\n \"returnType\": {\n \"type\": \"boolean\"\n }\n },\n \"lvpPublishCredential\": {\n \"description\": \"Publish a credential as a LinkedVP by adding it to the holder's DID Document\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/PublishCredentialArgs\"\n },\n \"returnType\": {\n \"$ref\": \"#/components/schemas/LinkedVPEntry\"\n }\n },\n \"lvpUnpublishCredential\": {\n \"description\": \"Unpublish a credential by removing its LinkedVP entry from the DID Document\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/UnpublishCredentialArgs\"\n },\n \"returnType\": {\n \"type\": \"boolean\"\n }\n }\n }\n }\n }\n}","import { DigitalCredential } from '@sphereon/ssi-sdk.data-store-types'\nimport { IAgentPlugin } from '@veramo/core'\nimport { IsNull, Not } from 'typeorm'\nimport { schema } from '../index'\nimport { createLinkedVPPresentation } from '../services/LinkedVPService'\nimport {\n GeneratePresentationArgs,\n GetServiceEntriesArgs,\n HasLinkedVPEntryArgs,\n ILinkedVPManager,\n LinkedVPEntry,\n LinkedVPPresentation,\n LinkedVPServiceEntry,\n PublishCredentialArgs,\n RequiredContext,\n UnpublishCredentialArgs,\n} from '../types'\n\n// Exposing the methods here for any REST implementation\nexport const linkedVPManagerMethods: Array<string> = [\n 'lvpPublishCredential',\n 'lvpUnpublishCredential',\n 'lvpHasEntry',\n 'lvpGetServiceEntries',\n 'lvpGeneratePresentation',\n]\n\n/**\n * {@inheritDoc ILinkedVPManager}\n */\nexport class LinkedVPManager implements IAgentPlugin {\n readonly schema = schema.ILinkedVPManager\n readonly methods: ILinkedVPManager = {\n lvpPublishCredential: this.lvpPublishCredential.bind(this),\n lvpUnpublishCredential: this.lvpUnpublishCredential.bind(this),\n lvpHasEntry: this.lvpHasEntry.bind(this),\n lvpGetServiceEntries: this.lvpGetServiceEntries.bind(this),\n lvpGeneratePresentation: this.lvpGeneratePresentation.bind(this),\n }\n\n private readonly holderDids: Record<string, string>\n\n constructor(options: { holderDids: Record<string, string> }) {\n this.holderDids = options.holderDids\n }\n\n private async lvpPublishCredential(args: PublishCredentialArgs, context: RequiredContext): Promise<LinkedVPEntry> {\n const { digitalCredentialId } = args\n\n const credential: DigitalCredential = await context.agent.crsGetCredential({ id: digitalCredentialId })\n\n if (credential.linkedVpId) {\n return Promise.reject(new Error(`Credential ${digitalCredentialId} is already published with linkedVpId ${credential.linkedVpId}`))\n }\n\n const linkedVpId = this.buildLinkedVpId(args.linkedVpId, credential.tenantId)\n\n await this.ensureLinkedVpIdUnique(linkedVpId, context, credential.tenantId)\n\n const publishedAt = new Date()\n await context.agent.crsUpdateCredential({\n id: digitalCredentialId,\n linkedVpId,\n linkedVpFrom: publishedAt,\n })\n\n return {\n id: credential.id,\n linkedVpId,\n tenantId: credential.tenantId,\n linkedVpFrom: publishedAt,\n createdAt: credential.createdAt,\n }\n }\n\n private async lvpUnpublishCredential(args: UnpublishCredentialArgs, context: RequiredContext): Promise<boolean> {\n const { linkedVpId } = args\n\n // Find credential by linkedVpId and tenantId\n const credentials = await context.agent.crsGetCredentials({\n filter: [{ linkedVpId }],\n })\n if (credentials.length === 0) {\n return Promise.reject(Error(`No credential found with linkedVpId ${linkedVpId}`))\n }\n\n const credential = credentials[0]\n await context.agent.crsUpdateCredential({\n id: credential.id,\n linkedVpId: undefined,\n linkedVpFrom: undefined,\n })\n\n return true\n }\n\n private async lvpHasEntry(args: HasLinkedVPEntryArgs, context: RequiredContext): Promise<boolean> {\n const { linkedVpId } = args\n\n try {\n const credentials = await context.agent.crsGetCredentials({\n filter: [{ linkedVpId }],\n })\n return credentials.length > 0\n } catch (error) {\n return false\n }\n }\n\n private async lvpGetServiceEntries(args: GetServiceEntriesArgs, context: RequiredContext): Promise<Array<LinkedVPServiceEntry>> {\n const { tenantId } = args\n\n // Get all published credentials (credentials with linkedVpId set)\n const filter: any = { linkedVpId: Not(IsNull()) }\n if (tenantId) {\n filter.tenantId = tenantId\n }\n\n const credentials = await context.agent.crsGetCredentials({\n filter: [filter],\n })\n\n return credentials\n .filter((cred) => cred.linkedVpId !== undefined && cred.linkedVpId !== null)\n .map((cred) => {\n const holderDidForEntry = this.getHolderDid(cred.tenantId)\n return this.credentialToServiceEntry(cred, holderDidForEntry)\n })\n }\n\n private async lvpGeneratePresentation(args: GeneratePresentationArgs, context: RequiredContext): Promise<LinkedVPPresentation> {\n const { linkedVpId } = args\n const tenantId = this.parseTenantFromLinkedVpId(linkedVpId)\n const holderDid = this.getHolderDid(tenantId)\n\n const uniqueCredentials = await context.agent.crsGetUniqueCredentials({\n filter: [\n {\n linkedVpId: args.linkedVpId,\n ...(tenantId && { tenantId }),\n },\n ],\n })\n if (uniqueCredentials.length === 0) {\n return Promise.reject(Error(`No published credentials found for linkedVpId ${linkedVpId}`))\n }\n\n // Generate the Verifiable Presentation with all published credentials\n return createLinkedVPPresentation(holderDid, uniqueCredentials, context.agent)\n }\n\n private getHolderDid(tenantId: string | undefined) {\n const holderDid = this.holderDids[tenantId ?? 'default']\n if (!holderDid) {\n throw Error(`No holder did supplied for tenant ${tenantId ?? 'default'}`)\n }\n return holderDid\n }\n\n private parseTenantFromLinkedVpId(linkedVpId: string): string | undefined {\n const idx = linkedVpId.lastIndexOf('@')\n return idx === -1 ? undefined : linkedVpId.substring(idx + 1)\n }\n\n private generateLinkedVpId(): string {\n return `lvp-${Date.now()}-${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 { Loggers, OriginalVerifiableCredential, WrappedVerifiableCredential } from '@sphereon/ssi-types'\nimport type { PresentationPayload } from '@veramo/core'\nimport { W3CVerifiableCredential } from '@veramo/core/src/types/vc-data-model'\nimport { LOGGER_NAMESPACE, RequiredContext } from '../types'\n\nconst logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE)\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 credentials: UniqueDigitalCredential[],\n agent: RequiredContext['agent'],\n): Promise<string | Record<string, any>> {\n if (credentials.length === 0) {\n return Promise.reject(Error('Cannot create LinkedVP presentation with zero credentials'))\n }\n\n logger.debug(`Creating LinkedVP presentation for ${holderDid} with ${credentials.length} credentials`)\n\n const identifier = await agent.identifierManagedGet({ identifier: holderDid })\n\n // Extract and prepare credentials\n const verifiableCredentials = credentials.map((credential) => {\n const original = extractOriginalCredential(credential)\n // Keep as-is if string (JWT), otherwise convert to object\n return typeof original === 'string' ? original : original\n })\n\n // Create VP structure\n const vpObject: PresentationPayload = {\n '@context': ['https://www.w3.org/2018/credentials/v1'],\n type: ['VerifiablePresentation'],\n holder: holderDid,\n verifiableCredential: verifiableCredentials as W3CVerifiableCredential[],\n }\n\n // Create and sign the VP as JWT\n const result = await agent.createVerifiablePresentation({\n presentation: vpObject,\n proofFormat: 'jwt',\n keyRef: identifier.kmsKeyRef || identifier.kid,\n })\n\n // Extract JWT from result\n if (typeof result === 'string') {\n return result\n }\n\n if (result.proof && 'jws' in result.proof) {\n return result.proof.jws\n }\n\n return Promise.reject(Error('Failed to create JWT VP - no JWT in result'))\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 { 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 = string | Record<string, any>\n\nexport interface ILinkedVPManager extends IPluginMethodMap {\n /**\n * Publish a credential as a LinkedVP by adding it to the holder's DID Document\n * @param args - Publication arguments including credential ID and scope configuration\n * @param context - Agent context\n */\n lvpPublishCredential(args: PublishCredentialArgs, context: RequiredContext): Promise<LinkedVPEntry>\n\n /**\n * Unpublish a credential by removing its LinkedVP entry from the DID Document\n * @param args - Unpublish arguments\n * @param context - Agent context\n */\n lvpUnpublishCredential(args: UnpublishCredentialArgs, context: RequiredContext): Promise<boolean>\n\n /**\n * Check if a LinkedVP entry exists by linkedVpId\n * @param args - Query arguments\n * @param context - Agent context\n */\n lvpHasEntry(args: HasLinkedVPEntryArgs, context: RequiredContext): Promise<boolean>\n\n /**\n * Get LinkedVP service entries for a DID to be added to a DID Document\n * This is useful when generating DID Documents with toDidDocument\n * @param args - Query arguments for the DID\n * @param context - Agent context\n */\n lvpGetServiceEntries(args: GetServiceEntriesArgs, context: RequiredContext): Promise<Array<LinkedVPServiceEntry>>\n\n /**\n * Generate and return a Verifiable Presentation for a published LinkedVP\n * This is the main endpoint handler for GET /linked-vp/{linkedVpId}\n * @param args - Generation arguments\n * @param context - Agent context\n */\n lvpGeneratePresentation(args: GeneratePresentationArgs, context: RequiredContext): Promise<LinkedVPPresentation>\n}\n\nexport type PublishCredentialArgs = {\n digitalCredentialId: string\n linkedVpId?: string // Optional: if not provided, will be auto-generated\n}\n\nexport type UnpublishCredentialArgs = {\n linkedVpId: string\n}\n\nexport type HasLinkedVPEntryArgs = {\n linkedVpId: string\n}\n\nexport type GetServiceEntriesArgs = {\n tenantId?: string\n}\n\nexport type GeneratePresentationArgs = {\n linkedVpId: string\n}\n\nexport type LinkedVPEntry = {\n id: string\n linkedVpId: string\n tenantId?: string\n linkedVpFrom?: Date\n createdAt: Date\n}\n\nexport type LinkedVPServiceEntry = {\n id: string\n type: 'LinkedVerifiablePresentation'\n serviceEndpoint: string\n}\n\nexport type RequiredContext = IAgentContext<IIdentifierResolution & ICredentialStore & IKeyManager & VcdmCredentialPlugin>\n"],"mappings":"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","names":["IsNull","Not","Loggers","LOGGER_NAMESPACE","logger","Loggers","DEFAULT","get","LOGGER_NAMESPACE","extractOriginalCredential","credential","udc","originalVerifiableCredential","uniformVerifiableCredential","original","createLinkedVPPresentation","holderDid","credentials","agent","length","Promise","reject","Error","debug","identifier","identifierManagedGet","verifiableCredentials","map","vpObject","type","holder","verifiableCredential","result","createVerifiablePresentation","presentation","proofFormat","keyRef","kmsKeyRef","kid","proof","jws","linkedVPManagerMethods","LinkedVPManager","schema","ILinkedVPManager","methods","lvpPublishCredential","bind","lvpUnpublishCredential","lvpHasEntry","lvpGetServiceEntries","lvpGeneratePresentation","holderDids","options","args","context","digitalCredentialId","credential","agent","crsGetCredential","id","linkedVpId","Promise","reject","Error","buildLinkedVpId","tenantId","ensureLinkedVpIdUnique","publishedAt","Date","crsUpdateCredential","linkedVpFrom","createdAt","credentials","crsGetCredentials","filter","length","undefined","error","Not","IsNull","cred","map","holderDidForEntry","getHolderDid","credentialToServiceEntry","parseTenantFromLinkedVpId","holderDid","uniqueCredentials","crsGetUniqueCredentials","createLinkedVPPresentation","idx","lastIndexOf","substring","generateLinkedVpId","now","Math","random","toString","finalLinkedVpId","includes","getBaseUrlFromDid","startsWith","withoutPrefix","replace","parts","split","domain","shift","path","join","buildServiceEndpoint","baseUrl","cleanBaseUrl","endsWith","slice","type","serviceEndpoint"]}
1
+ {"version":3,"sources":["../plugin.schema.json","../src/agent/LinkedVPManager.ts","../src/services/LinkedVPService.ts","../src/types/ILinkedVPManager.ts"],"sourcesContent":["{\n \"ILinkedVPManager\": {\n \"components\": {\n \"schemas\": {\n \"GeneratePresentationArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"linkedVpId\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"linkedVpId\"\n ],\n \"additionalProperties\": false\n },\n \"LinkedVPPresentation\": {\n \"anyOf\": [\n {\n \"type\": \"string\"\n },\n {\n \"$ref\": \"#/components/schemas/Record<string,any>\"\n }\n ]\n },\n \"Record<string,any>\": {\n \"type\": \"object\"\n },\n \"GetServiceEntriesArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"tenantId\": {\n \"type\": \"string\"\n }\n },\n \"additionalProperties\": false\n },\n \"LinkedVPServiceEntry\": {\n \"type\": \"object\",\n \"properties\": {\n \"id\": {\n \"type\": \"string\"\n },\n \"type\": {\n \"type\": \"string\",\n \"const\": \"LinkedVerifiablePresentation\"\n },\n \"serviceEndpoint\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"id\",\n \"type\",\n \"serviceEndpoint\"\n ],\n \"additionalProperties\": false\n },\n \"HasLinkedVPEntryArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"linkedVpId\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"linkedVpId\"\n ],\n \"additionalProperties\": false\n },\n \"PublishCredentialArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"digitalCredentialId\": {\n \"type\": \"string\"\n },\n \"linkedVpId\": {\n \"type\": \"string\"\n },\n \"tenantId\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"digitalCredentialId\"\n ],\n \"additionalProperties\": false\n },\n \"LinkedVPEntry\": {\n \"type\": \"object\",\n \"properties\": {\n \"id\": {\n \"type\": \"string\"\n },\n \"linkedVpId\": {\n \"type\": \"string\"\n },\n \"tenantId\": {\n \"type\": \"string\"\n },\n \"linkedVpFrom\": {\n \"type\": \"string\",\n \"format\": \"date-time\"\n },\n \"createdAt\": {\n \"type\": \"string\",\n \"format\": \"date-time\"\n }\n },\n \"required\": [\n \"id\",\n \"linkedVpId\",\n \"createdAt\"\n ],\n \"additionalProperties\": false\n },\n \"UnpublishCredentialArgs\": {\n \"type\": \"object\",\n \"properties\": {\n \"linkedVpId\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"linkedVpId\"\n ],\n \"additionalProperties\": false\n }\n },\n \"methods\": {\n \"lvpGeneratePresentation\": {\n \"description\": \"Generate and return a Verifiable Presentation for a published LinkedVP This is the main endpoint handler for GET /linked-vp/\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/GeneratePresentationArgs\"\n },\n \"returnType\": {\n \"$ref\": \"#/components/schemas/LinkedVPPresentation\"\n }\n },\n \"lvpGetServiceEntries\": {\n \"description\": \"Get LinkedVP service entries for a DID to be added to a DID Document This is useful when generating DID Documents with toDidDocument\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/GetServiceEntriesArgs\"\n },\n \"returnType\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/components/schemas/LinkedVPServiceEntry\"\n }\n }\n },\n \"lvpHasEntry\": {\n \"description\": \"Check if a LinkedVP entry exists by linkedVpId\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/HasLinkedVPEntryArgs\"\n },\n \"returnType\": {\n \"type\": \"boolean\"\n }\n },\n \"lvpPublishCredential\": {\n \"description\": \"Publish a credential as a LinkedVP by adding it to the holder's DID Document\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/PublishCredentialArgs\"\n },\n \"returnType\": {\n \"$ref\": \"#/components/schemas/LinkedVPEntry\"\n }\n },\n \"lvpUnpublishCredential\": {\n \"description\": \"Unpublish a credential by removing its LinkedVP entry from the DID Document\",\n \"arguments\": {\n \"$ref\": \"#/components/schemas/UnpublishCredentialArgs\"\n },\n \"returnType\": {\n \"type\": \"boolean\"\n }\n }\n }\n }\n }\n}","import { DigitalCredential } from '@sphereon/ssi-sdk.data-store-types'\nimport { IAgentPlugin } from '@veramo/core'\nimport { IsNull, Not } from 'typeorm'\nimport { schema } from '../index'\nimport { createLinkedVPPresentation } from '../services/LinkedVPService'\nimport {\n GeneratePresentationArgs,\n GetServiceEntriesArgs,\n HasLinkedVPEntryArgs,\n ILinkedVPManager,\n LinkedVPEntry,\n LinkedVPPresentation,\n LinkedVPServiceEntry,\n PublishCredentialArgs,\n RequiredContext,\n UnpublishCredentialArgs,\n} from '../types'\n\n// Exposing the methods here for any REST implementation\nexport const linkedVPManagerMethods: Array<string> = [\n 'lvpPublishCredential',\n 'lvpUnpublishCredential',\n 'lvpHasEntry',\n 'lvpGetServiceEntries',\n 'lvpGeneratePresentation',\n]\n\n/**\n * {@inheritDoc ILinkedVPManager}\n */\nexport class LinkedVPManager implements IAgentPlugin {\n readonly schema = schema.ILinkedVPManager\n readonly methods: ILinkedVPManager = {\n lvpPublishCredential: this.lvpPublishCredential.bind(this),\n lvpUnpublishCredential: this.lvpUnpublishCredential.bind(this),\n lvpHasEntry: this.lvpHasEntry.bind(this),\n lvpGetServiceEntries: this.lvpGetServiceEntries.bind(this),\n lvpGeneratePresentation: this.lvpGeneratePresentation.bind(this),\n }\n\n private readonly holderDids: Record<string, string>\n\n constructor(options: { holderDids: Record<string, string> }) {\n this.holderDids = options.holderDids\n }\n\n private async lvpPublishCredential(args: PublishCredentialArgs, context: RequiredContext): Promise<LinkedVPEntry> {\n const { digitalCredentialId } = args\n\n const credential: DigitalCredential = await context.agent.crsGetCredential({ id: digitalCredentialId })\n\n if (credential.linkedVpId) {\n return Promise.reject(new Error(`Credential ${digitalCredentialId} is already published with linkedVpId ${credential.linkedVpId}`))\n }\n\n const linkedVpId = this.buildLinkedVpId(args.linkedVpId, credential.tenantId)\n\n await this.ensureLinkedVpIdUnique(linkedVpId, context, credential.tenantId)\n\n const publishedAt = new Date()\n await context.agent.crsUpdateCredential({\n id: digitalCredentialId,\n linkedVpId,\n linkedVpFrom: publishedAt,\n })\n\n return {\n id: credential.id,\n linkedVpId,\n tenantId: credential.tenantId,\n linkedVpFrom: publishedAt,\n createdAt: credential.createdAt,\n }\n }\n\n private async lvpUnpublishCredential(args: UnpublishCredentialArgs, context: RequiredContext): Promise<boolean> {\n const { linkedVpId } = args\n\n // Find credential by linkedVpId and tenantId\n const credentials = await context.agent.crsGetCredentials({\n filter: [{ linkedVpId }],\n })\n if (credentials.length === 0) {\n return Promise.reject(Error(`No credential found with linkedVpId ${linkedVpId}`))\n }\n\n const credential = credentials[0]\n await context.agent.crsUpdateCredential({\n id: credential.id,\n linkedVpId: undefined,\n linkedVpFrom: undefined,\n })\n\n return true\n }\n\n private async lvpHasEntry(args: HasLinkedVPEntryArgs, context: RequiredContext): Promise<boolean> {\n const { linkedVpId } = args\n\n try {\n const credentials = await context.agent.crsGetCredentials({\n filter: [{ linkedVpId }],\n })\n return credentials.length > 0\n } catch (error) {\n return false\n }\n }\n\n private async lvpGetServiceEntries(args: GetServiceEntriesArgs, context: RequiredContext): Promise<Array<LinkedVPServiceEntry>> {\n const { tenantId } = args\n\n // Get all published credentials (credentials with linkedVpId set)\n const filter: any = { linkedVpId: Not(IsNull()) }\n if (tenantId) {\n filter.tenantId = tenantId\n }\n\n const credentials = await context.agent.crsGetCredentials({\n filter: [filter],\n })\n\n return credentials\n .filter((cred) => cred.linkedVpId !== undefined && cred.linkedVpId !== null)\n .map((cred) => {\n const holderDidForEntry = this.getHolderDid(cred.tenantId)\n return this.credentialToServiceEntry(cred, holderDidForEntry)\n })\n }\n\n private async lvpGeneratePresentation(args: GeneratePresentationArgs, context: RequiredContext): Promise<LinkedVPPresentation> {\n const { linkedVpId } = args\n const tenantId = this.parseTenantFromLinkedVpId(linkedVpId)\n const holderDid = this.getHolderDid(tenantId)\n\n const uniqueCredentials = await context.agent.crsGetUniqueCredentials({\n filter: [\n {\n linkedVpId: args.linkedVpId,\n ...(tenantId && { tenantId }),\n },\n ],\n })\n if (uniqueCredentials.length === 0) {\n return Promise.reject(Error(`No published credentials found for linkedVpId ${linkedVpId}`))\n }\n if (uniqueCredentials.length > 1) {\n return Promise.reject(Error(`Multiple credentials found for linkedVpId ${linkedVpId}`))\n }\n\n // Generate the Verifiable Presentation with all published credentials\n return createLinkedVPPresentation(holderDid, uniqueCredentials[0], context.agent)\n }\n\n private getHolderDid(tenantId: string | undefined) {\n const holderDid = this.holderDids[tenantId ?? 'default']\n if (!holderDid) {\n throw Error(`No holder did supplied for tenant ${tenantId ?? 'default'}`)\n }\n return holderDid\n }\n\n private parseTenantFromLinkedVpId(linkedVpId: string): string | undefined {\n const idx = linkedVpId.lastIndexOf('@')\n return idx === -1 ? undefined : linkedVpId.substring(idx + 1)\n }\n\n private generateLinkedVpId(): string {\n return `lvp-${Date.now()}-${Math.random().toString(36).substring(2, 15)}`\n }\n\n private async ensureLinkedVpIdUnique(linkedVpId: string, context: RequiredContext, tenantId?: string): Promise<void> {\n const credentials = await context.agent.crsGetCredentials({\n filter: [{ linkedVpId, ...(tenantId && { tenantId }) }],\n })\n\n if (credentials.length > 0) {\n throw new Error(`LinkedVP ID ${linkedVpId} already exists${tenantId ? ` for tenant ${tenantId}` : ''}`)\n }\n }\n\n private buildLinkedVpId(linkedVpId: string | undefined, tenantId: string | undefined) {\n let finalLinkedVpId = linkedVpId || this.generateLinkedVpId()\n\n // Append tenantId if provided and not already present\n if (tenantId && tenantId !== '' && !finalLinkedVpId.includes('@')) {\n finalLinkedVpId = `${finalLinkedVpId}@${tenantId}`\n }\n return finalLinkedVpId\n }\n\n private getBaseUrlFromDid(holderDid: string): string {\n if (!holderDid.startsWith('did:web:')) {\n throw new Error(`Invalid DID: ${holderDid}, must be did:web`)\n }\n\n const withoutPrefix = holderDid.replace('did:web:', '') // example.com:tenants:tenant1\n const parts = withoutPrefix.split(':')\n const domain = parts.shift()! // example.com\n const path = parts.join('/') // tenants/tenant1\n\n return path\n ? `https://${domain}/${path}` // https://example.com/tenants/tenant1\n : `https://${domain}` // https://example.com\n }\n\n private buildServiceEndpoint(holderDid: string, linkedVpId: string): string {\n const baseUrl = this.getBaseUrlFromDid(holderDid)\n const cleanBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl\n return `${cleanBaseUrl}/linked-vp/${linkedVpId}`\n }\n\n private credentialToServiceEntry(credential: DigitalCredential, holderDid: string): LinkedVPServiceEntry {\n if (!credential.linkedVpId) {\n throw new Error(`Credential ${credential.id} does not have a linkedVpId`)\n }\n\n return {\n id: `${holderDid}#${credential.linkedVpId}`,\n type: 'LinkedVerifiablePresentation',\n serviceEndpoint: this.buildServiceEndpoint(holderDid, credential.linkedVpId),\n }\n }\n}\n","import { UniqueDigitalCredential } from '@sphereon/ssi-sdk.credential-store'\nimport { calculateSdHash, defaultGenerateDigest, PartialSdJwtKbJwt } from '@sphereon/ssi-sdk.sd-jwt'\n\nimport {\n CredentialMapper,\n DocumentFormat,\n Loggers,\n OriginalVerifiableCredential,\n SdJwtDecodedVerifiableCredential,\n WrappedVerifiableCredential,\n} from '@sphereon/ssi-types'\nimport { LinkedVPPresentation, LOGGER_NAMESPACE, RequiredContext } from '../types'\n\nconst logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE)\nconst CLOCK_SKEW = 120 // TODO make adjustable?\n\n/**\n * Extracts the original credential from various wrapper types\n */\nfunction extractOriginalCredential(\n credential: UniqueDigitalCredential | WrappedVerifiableCredential | OriginalVerifiableCredential,\n): OriginalVerifiableCredential {\n if (typeof credential === 'string') {\n return credential\n }\n\n if ('digitalCredential' in credential) {\n const udc = credential as UniqueDigitalCredential\n if (udc.originalVerifiableCredential) {\n return udc.originalVerifiableCredential\n }\n return udc.uniformVerifiableCredential as OriginalVerifiableCredential\n }\n\n if ('original' in credential) {\n return credential.original\n }\n\n return credential as OriginalVerifiableCredential\n}\n\n/**\n * Creates a Verifiable Presentation for LinkedVP publishing\n * Contains multiple credentials in a single JWT VP\n * No nonce or audience since this is for publishing, not responding to verification\n */\nexport async function createLinkedVPPresentation(\n holderDid: string,\n credential: UniqueDigitalCredential,\n agent: RequiredContext['agent'],\n): Promise<LinkedVPPresentation> {\n logger.debug(`Creating LinkedVP presentation for ${holderDid} of credential ${credential.id}`)\n\n const identifier = await agent.identifierManagedGet({ identifier: holderDid })\n const originalCredential = extractOriginalCredential(credential)\n const documentFormat = CredentialMapper.detectDocumentType(originalCredential)\n switch (documentFormat) {\n case DocumentFormat.SD_JWT_VC: {\n // SD-JWT with KB-JWT\n const decodedSdJwt = await CredentialMapper.decodeSdJwtVcAsync(\n typeof originalCredential === 'string' ? originalCredential : (originalCredential as SdJwtDecodedVerifiableCredential).compactSdJwtVc,\n defaultGenerateDigest,\n )\n\n const hashAlg = decodedSdJwt.signedPayload._sd_alg ?? 'sha-256'\n const sdHash = calculateSdHash(decodedSdJwt.compactSdJwtVc, hashAlg, defaultGenerateDigest)\n const kbJwtPayload: PartialSdJwtKbJwt['payload'] = {\n iat: Math.floor(Date.now() / 1000 - CLOCK_SKEW),\n sd_hash: sdHash,\n }\n\n const presentationResult = await agent.createSdJwtPresentation({\n presentation: decodedSdJwt.compactSdJwtVc,\n kb: {\n payload: kbJwtPayload as any, // FIXME?\n },\n })\n\n return {\n documentFormat,\n presentationPayload: presentationResult.presentation,\n }\n }\n case DocumentFormat.JSONLD: {\n // JSON-LD VC - create JSON-LD VP with challenge and domain in proof\n const vcObject = typeof originalCredential === 'string' ? JSON.parse(originalCredential) : originalCredential\n\n const vpObject = {\n '@context': ['https://www.w3.org/2018/credentials/v1'],\n type: ['VerifiablePresentation'],\n verifiableCredential: [vcObject],\n holder: holderDid,\n }\n\n // Create JSON-LD VP with proof\n const verifiablePresentationSP = await agent.createVerifiablePresentation({\n presentation: vpObject,\n proofFormat: 'lds',\n keyRef: identifier.kmsKeyRef || identifier.kid,\n })\n return {\n documentFormat,\n presentationPayload: verifiablePresentationSP,\n }\n }\n case DocumentFormat.MSO_MDOC: {\n // ISO mdoc - create mdoc VP token\n // This is a placeholder implementation\n // Full implementation would require:\n // 1. Decode the mdoc using CredentialMapper or mdoc utilities\n // 2. Build proper mdoc VP token with session transcript\n // 3. Include nonce/audience in the session transcript\n logger.warning('mso_mdoc format has basic support - production use requires proper mdoc VP token implementation')\n\n return {\n documentFormat,\n presentationPayload: originalCredential,\n }\n }\n default: {\n // JWT VC - create JWT VP with nonce and aud in payload\n const vcJwt = typeof originalCredential === 'string' ? originalCredential : JSON.stringify(originalCredential)\n\n // Create VP JWT using agent method\n const vpPayload = {\n iss: holderDid,\n vp: {\n '@context': ['https://www.w3.org/2018/credentials/v1'],\n type: ['VerifiablePresentation'],\n holder: holderDid,\n verifiableCredential: [vcJwt],\n },\n iat: Math.floor(Date.now() / 1000 - CLOCK_SKEW),\n exp: Math.floor(Date.now() / 1000 + 600 + CLOCK_SKEW), // 10 minutes\n }\n\n // Use the agent's JWT creation capability\n const vpJwt = await agent.createVerifiablePresentation({\n presentation: vpPayload.vp,\n proofFormat: 'jwt',\n keyRef: identifier.kmsKeyRef || identifier.kid,\n })\n\n return {\n documentFormat,\n presentationPayload: (vpJwt.proof && 'jwt' in vpJwt.proof && vpJwt.proof.jwt) || vpJwt,\n }\n }\n }\n}\n","import { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'\nimport { ICredentialStore } from '@sphereon/ssi-sdk.credential-store'\nimport { VcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'\nimport { ISDJwtPlugin } from '@sphereon/ssi-sdk.sd-jwt'\nimport { DocumentFormat } from '@sphereon/ssi-types'\nimport { IAgentContext, IPluginMethodMap } from '@veramo/core'\nimport { IKeyManager } from '@veramo/core/src/types/IKeyManager'\n\nexport const LOGGER_NAMESPACE = 'sphereon:linked-vp'\n\nexport type LinkedVPPresentation = {\n documentFormat: DocumentFormat\n presentationPayload: string | Record<string, any>\n}\n\nexport interface ILinkedVPManager extends IPluginMethodMap {\n /**\n * Publish a credential as a LinkedVP by adding it to the holder's DID Document\n * @param args - Publication arguments including credential ID and scope configuration\n * @param context - Agent context\n */\n lvpPublishCredential(args: PublishCredentialArgs, context: RequiredContext): Promise<LinkedVPEntry>\n\n /**\n * Unpublish a credential by removing its LinkedVP entry from the DID Document\n * @param args - Unpublish arguments\n * @param context - Agent context\n */\n lvpUnpublishCredential(args: UnpublishCredentialArgs, context: RequiredContext): Promise<boolean>\n\n /**\n * Check if a LinkedVP entry exists by linkedVpId\n * @param args - Query arguments\n * @param context - Agent context\n */\n lvpHasEntry(args: HasLinkedVPEntryArgs, context: RequiredContext): Promise<boolean>\n\n /**\n * Get LinkedVP service entries for a DID to be added to a DID Document\n * This is useful when generating DID Documents with toDidDocument\n * @param args - Query arguments for the DID\n * @param context - Agent context\n */\n lvpGetServiceEntries(args: GetServiceEntriesArgs, context: RequiredContext): Promise<Array<LinkedVPServiceEntry>>\n\n /**\n * Generate and return a Verifiable Presentation for a published LinkedVP\n * This is the main endpoint handler for GET /linked-vp/{linkedVpId}\n * @param args - Generation arguments\n * @param context - Agent context\n */\n lvpGeneratePresentation(args: GeneratePresentationArgs, context: RequiredContext): Promise<LinkedVPPresentation>\n}\n\nexport type PublishCredentialArgs = {\n digitalCredentialId: string\n linkedVpId?: string // Optional: if not provided, will be auto-generated\n}\n\nexport type UnpublishCredentialArgs = {\n linkedVpId: string\n}\n\nexport type HasLinkedVPEntryArgs = {\n linkedVpId: string\n}\n\nexport type GetServiceEntriesArgs = {\n tenantId?: string\n}\n\nexport type GeneratePresentationArgs = {\n linkedVpId: string\n}\n\nexport type LinkedVPEntry = {\n id: string\n linkedVpId: string\n tenantId?: string\n linkedVpFrom?: Date\n createdAt: Date\n}\n\nexport type LinkedVPServiceEntry = {\n id: string\n type: 'LinkedVerifiablePresentation'\n serviceEndpoint: string\n}\n\nexport type RequiredContext = IAgentContext<IIdentifierResolution & ICredentialStore & IKeyManager & VcdmCredentialPlugin & ISDJwtPlugin>\n"],"mappings":"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","names":["IsNull","Not","calculateSdHash","defaultGenerateDigest","CredentialMapper","DocumentFormat","Loggers","LOGGER_NAMESPACE","logger","Loggers","DEFAULT","get","LOGGER_NAMESPACE","CLOCK_SKEW","extractOriginalCredential","credential","udc","originalVerifiableCredential","uniformVerifiableCredential","original","createLinkedVPPresentation","holderDid","agent","debug","id","identifier","identifierManagedGet","originalCredential","documentFormat","CredentialMapper","detectDocumentType","DocumentFormat","SD_JWT_VC","decodedSdJwt","decodeSdJwtVcAsync","compactSdJwtVc","defaultGenerateDigest","hashAlg","signedPayload","_sd_alg","sdHash","calculateSdHash","kbJwtPayload","iat","Math","floor","Date","now","sd_hash","presentationResult","createSdJwtPresentation","presentation","kb","payload","presentationPayload","JSONLD","vcObject","JSON","parse","vpObject","type","verifiableCredential","holder","verifiablePresentationSP","createVerifiablePresentation","proofFormat","keyRef","kmsKeyRef","kid","MSO_MDOC","warning","vcJwt","stringify","vpPayload","iss","vp","exp","vpJwt","proof","jwt","linkedVPManagerMethods","LinkedVPManager","schema","ILinkedVPManager","methods","lvpPublishCredential","bind","lvpUnpublishCredential","lvpHasEntry","lvpGetServiceEntries","lvpGeneratePresentation","holderDids","options","args","context","digitalCredentialId","credential","agent","crsGetCredential","id","linkedVpId","Promise","reject","Error","buildLinkedVpId","tenantId","ensureLinkedVpIdUnique","publishedAt","Date","crsUpdateCredential","linkedVpFrom","createdAt","credentials","crsGetCredentials","filter","length","undefined","error","Not","IsNull","cred","map","holderDidForEntry","getHolderDid","credentialToServiceEntry","parseTenantFromLinkedVpId","holderDid","uniqueCredentials","crsGetUniqueCredentials","createLinkedVPPresentation","idx","lastIndexOf","substring","generateLinkedVpId","now","Math","random","toString","finalLinkedVpId","includes","getBaseUrlFromDid","startsWith","withoutPrefix","replace","parts","split","domain","shift","path","join","buildServiceEndpoint","baseUrl","cleanBaseUrl","endsWith","slice","type","serviceEndpoint"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sphereon/ssi-sdk.linked-vp",
3
- "version": "0.34.1-feature.SSISDK.82.linkedVP.325+9de5d4ff",
3
+ "version": "0.34.1-feature.SSISDK.82.linkedVP.327+81517113",
4
4
  "source": "src/index.ts",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -27,12 +27,13 @@
27
27
  "generate-plugin-schema": "tsx ../../packages/dev/bin/sphereon.js dev generate-plugin-schema"
28
28
  },
29
29
  "dependencies": {
30
- "@sphereon/ssi-sdk-ext.identifier-resolution": "0.34.1-feature.SSISDK.82.linkedVP.325+9de5d4ff",
31
- "@sphereon/ssi-sdk.credential-store": "0.34.1-feature.SSISDK.82.linkedVP.325+9de5d4ff",
32
- "@sphereon/ssi-sdk.credential-vcdm": "0.34.1-feature.SSISDK.82.linkedVP.325+9de5d4ff",
33
- "@sphereon/ssi-sdk.data-store": "0.34.1-feature.SSISDK.82.linkedVP.325+9de5d4ff",
34
- "@sphereon/ssi-sdk.data-store-types": "0.34.1-feature.SSISDK.82.linkedVP.325+9de5d4ff",
35
- "@sphereon/ssi-types": "0.34.1-feature.SSISDK.82.linkedVP.325+9de5d4ff",
30
+ "@sphereon/ssi-sdk-ext.identifier-resolution": "0.34.1-feature.SSISDK.82.linkedVP.327+81517113",
31
+ "@sphereon/ssi-sdk.credential-store": "0.34.1-feature.SSISDK.82.linkedVP.327+81517113",
32
+ "@sphereon/ssi-sdk.credential-vcdm": "0.34.1-feature.SSISDK.82.linkedVP.327+81517113",
33
+ "@sphereon/ssi-sdk.data-store": "0.34.1-feature.SSISDK.82.linkedVP.327+81517113",
34
+ "@sphereon/ssi-sdk.data-store-types": "0.34.1-feature.SSISDK.82.linkedVP.327+81517113",
35
+ "@sphereon/ssi-sdk.sd-jwt": "0.34.1-feature.SSISDK.82.linkedVP.327+81517113",
36
+ "@sphereon/ssi-types": "0.34.1-feature.SSISDK.82.linkedVP.327+81517113",
36
37
  "@veramo/utils": "4.2.0",
37
38
  "cross-fetch": "^4.1.0",
38
39
  "dcql": "1.0.1",
@@ -41,10 +42,10 @@
41
42
  "uint8arrays": "3.1.1"
42
43
  },
43
44
  "devDependencies": {
44
- "@sphereon/ssi-sdk-ext.key-manager": "0.34.1-feature.SSISDK.82.linkedVP.325+9de5d4ff",
45
- "@sphereon/ssi-sdk-ext.kms-local": "0.34.1-feature.SSISDK.82.linkedVP.325+9de5d4ff",
46
- "@sphereon/ssi-sdk.agent-config": "0.34.1-feature.SSISDK.82.linkedVP.325+9de5d4ff",
47
- "@sphereon/ssi-sdk.credential-vcdm1-jwt-provider": "0.34.1-feature.SSISDK.82.linkedVP.325+9de5d4ff",
45
+ "@sphereon/ssi-sdk-ext.key-manager": "0.34.1-feature.SSISDK.82.linkedVP.327+81517113",
46
+ "@sphereon/ssi-sdk-ext.kms-local": "0.34.1-feature.SSISDK.82.linkedVP.327+81517113",
47
+ "@sphereon/ssi-sdk.agent-config": "0.34.1-feature.SSISDK.82.linkedVP.327+81517113",
48
+ "@sphereon/ssi-sdk.credential-vcdm1-jwt-provider": "0.34.1-feature.SSISDK.82.linkedVP.327+81517113",
48
49
  "@veramo/data-store": "4.2.0",
49
50
  "@veramo/did-manager": "4.2.0",
50
51
  "@veramo/did-provider-web": "4.2.0",
@@ -76,5 +77,5 @@
76
77
  "Presentation Defintion Manager",
77
78
  "PD Manager"
78
79
  ],
79
- "gitHead": "9de5d4ff0d17685351d63a9685ec853f6add2d6c"
80
+ "gitHead": "81517113906547b895df06ba83f283604b52ab2e"
80
81
  }
@@ -144,9 +144,12 @@ export class LinkedVPManager implements IAgentPlugin {
144
144
  if (uniqueCredentials.length === 0) {
145
145
  return Promise.reject(Error(`No published credentials found for linkedVpId ${linkedVpId}`))
146
146
  }
147
+ if (uniqueCredentials.length > 1) {
148
+ return Promise.reject(Error(`Multiple credentials found for linkedVpId ${linkedVpId}`))
149
+ }
147
150
 
148
151
  // Generate the Verifiable Presentation with all published credentials
149
- return createLinkedVPPresentation(holderDid, uniqueCredentials, context.agent)
152
+ return createLinkedVPPresentation(holderDid, uniqueCredentials[0], context.agent)
150
153
  }
151
154
 
152
155
  private getHolderDid(tenantId: string | undefined) {
@@ -1,10 +1,18 @@
1
1
  import { UniqueDigitalCredential } from '@sphereon/ssi-sdk.credential-store'
2
- import { Loggers, OriginalVerifiableCredential, WrappedVerifiableCredential } from '@sphereon/ssi-types'
3
- import type { PresentationPayload } from '@veramo/core'
4
- import { W3CVerifiableCredential } from '@veramo/core/src/types/vc-data-model'
5
- import { LOGGER_NAMESPACE, RequiredContext } from '../types'
2
+ import { calculateSdHash, defaultGenerateDigest, PartialSdJwtKbJwt } from '@sphereon/ssi-sdk.sd-jwt'
3
+
4
+ import {
5
+ CredentialMapper,
6
+ DocumentFormat,
7
+ Loggers,
8
+ OriginalVerifiableCredential,
9
+ SdJwtDecodedVerifiableCredential,
10
+ WrappedVerifiableCredential,
11
+ } from '@sphereon/ssi-types'
12
+ import { LinkedVPPresentation, LOGGER_NAMESPACE, RequiredContext } from '../types'
6
13
 
7
14
  const logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE)
15
+ const CLOCK_SKEW = 120 // TODO make adjustable?
8
16
 
9
17
  /**
10
18
  * Extracts the original credential from various wrapper types
@@ -38,47 +46,105 @@ function extractOriginalCredential(
38
46
  */
39
47
  export async function createLinkedVPPresentation(
40
48
  holderDid: string,
41
- credentials: UniqueDigitalCredential[],
49
+ credential: UniqueDigitalCredential,
42
50
  agent: RequiredContext['agent'],
43
- ): Promise<string | Record<string, any>> {
44
- if (credentials.length === 0) {
45
- return Promise.reject(Error('Cannot create LinkedVP presentation with zero credentials'))
46
- }
47
-
48
- logger.debug(`Creating LinkedVP presentation for ${holderDid} with ${credentials.length} credentials`)
51
+ ): Promise<LinkedVPPresentation> {
52
+ logger.debug(`Creating LinkedVP presentation for ${holderDid} of credential ${credential.id}`)
49
53
 
50
54
  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
+ )
51
64
 
52
- // Extract and prepare credentials
53
- const verifiableCredentials = credentials.map((credential) => {
54
- const original = extractOriginalCredential(credential)
55
- // Keep as-is if string (JWT), otherwise convert to object
56
- return typeof original === 'string' ? original : original
57
- })
58
-
59
- // Create VP structure
60
- const vpObject: PresentationPayload = {
61
- '@context': ['https://www.w3.org/2018/credentials/v1'],
62
- type: ['VerifiablePresentation'],
63
- holder: holderDid,
64
- verifiableCredential: verifiableCredentials as W3CVerifiableCredential[],
65
- }
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
+ }
66
71
 
67
- // Create and sign the VP as JWT
68
- const result = await agent.createVerifiablePresentation({
69
- presentation: vpObject,
70
- proofFormat: 'jwt',
71
- keyRef: identifier.kmsKeyRef || identifier.kid,
72
- })
72
+ const presentationResult = await agent.createSdJwtPresentation({
73
+ presentation: decodedSdJwt.compactSdJwtVc,
74
+ kb: {
75
+ payload: kbJwtPayload as any, // FIXME?
76
+ },
77
+ })
73
78
 
74
- // Extract JWT from result
75
- if (typeof result === 'string') {
76
- return result
77
- }
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
78
87
 
79
- if (result.proof && 'jws' in result.proof) {
80
- return result.proof.jws
81
- }
88
+ const vpObject = {
89
+ '@context': ['https://www.w3.org/2018/credentials/v1'],
90
+ type: ['VerifiablePresentation'],
91
+ verifiableCredential: [vcObject],
92
+ holder: holderDid,
93
+ }
82
94
 
83
- return Promise.reject(Error('Failed to create JWT VP - no JWT in result'))
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
+ }
149
+ }
84
150
  }
@@ -1,12 +1,17 @@
1
1
  import { IIdentifierResolution } from '@sphereon/ssi-sdk-ext.identifier-resolution'
2
2
  import { ICredentialStore } from '@sphereon/ssi-sdk.credential-store'
3
3
  import { VcdmCredentialPlugin } from '@sphereon/ssi-sdk.credential-vcdm'
4
+ import { ISDJwtPlugin } from '@sphereon/ssi-sdk.sd-jwt'
5
+ import { DocumentFormat } from '@sphereon/ssi-types'
4
6
  import { IAgentContext, IPluginMethodMap } from '@veramo/core'
5
7
  import { IKeyManager } from '@veramo/core/src/types/IKeyManager'
6
8
 
7
9
  export const LOGGER_NAMESPACE = 'sphereon:linked-vp'
8
10
 
9
- export type LinkedVPPresentation = string | Record<string, any>
11
+ export type LinkedVPPresentation = {
12
+ documentFormat: DocumentFormat
13
+ presentationPayload: string | Record<string, any>
14
+ }
10
15
 
11
16
  export interface ILinkedVPManager extends IPluginMethodMap {
12
17
  /**
@@ -82,4 +87,4 @@ export type LinkedVPServiceEntry = {
82
87
  serviceEndpoint: string
83
88
  }
84
89
 
85
- export type RequiredContext = IAgentContext<IIdentifierResolution & ICredentialStore & IKeyManager & VcdmCredentialPlugin>
90
+ export type RequiredContext = IAgentContext<IIdentifierResolution & ICredentialStore & IKeyManager & VcdmCredentialPlugin & ISDJwtPlugin>