@sphereon/ssi-sdk.linked-vp 0.34.1-feature.SSISDK.82.linkedVP.341 → 0.36.1-feature.SSISDK.70.integrate.digidentity.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,6 +14,37 @@ import { LinkedVPPresentation, LOGGER_NAMESPACE, RequiredContext } from '../type
14
14
  const logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE)
15
15
  const CLOCK_SKEW = 120 // TODO make adjustable?
16
16
 
17
+ /**
18
+ * Creates a Verifiable Presentation for LinkedVP publishing
19
+ * Contains multiple credentials in a single JWT VP
20
+ * No nonce or audience since this is for publishing, not responding to verification
21
+ */
22
+ export async function createLinkedVPPresentation(
23
+ holderDid: string,
24
+ credential: UniqueDigitalCredential,
25
+ agent: RequiredContext['agent'],
26
+ ): Promise<LinkedVPPresentation> {
27
+ logger.debug(`Creating LinkedVP presentation for ${holderDid} of credential ${credential.id}`)
28
+
29
+ const originalCredential = extractOriginalCredential(credential)
30
+ const documentFormat = CredentialMapper.detectDocumentType(originalCredential)
31
+
32
+ switch (documentFormat) {
33
+ case DocumentFormat.SD_JWT_VC: {
34
+ return createSdJwtPresentation(originalCredential, agent)
35
+ }
36
+ case DocumentFormat.JSONLD: {
37
+ return createJsonLdPresentation(holderDid, originalCredential, agent)
38
+ }
39
+ case DocumentFormat.MSO_MDOC: {
40
+ return createMdocPresentation(originalCredential)
41
+ }
42
+ default: {
43
+ return createJwtPresentation(holderDid, originalCredential, agent)
44
+ }
45
+ }
46
+ }
47
+
17
48
  /**
18
49
  * Extracts the original credential from various wrapper types
19
50
  */
@@ -40,111 +71,123 @@ function extractOriginalCredential(
40
71
  }
41
72
 
42
73
  /**
43
- * Creates a Verifiable Presentation for LinkedVP publishing
44
- * Contains multiple credentials in a single JWT VP
45
- * No nonce or audience since this is for publishing, not responding to verification
74
+ * Creates an SD-JWT presentation with KB-JWT
46
75
  */
47
- export async function createLinkedVPPresentation(
76
+ async function createSdJwtPresentation(
77
+ originalCredential: OriginalVerifiableCredential,
78
+ agent: RequiredContext['agent'],
79
+ ): Promise<LinkedVPPresentation> {
80
+ // SD-JWT with KB-JWT
81
+ const decodedSdJwt = await CredentialMapper.decodeSdJwtVcAsync(
82
+ typeof originalCredential === 'string' ? originalCredential : (originalCredential as SdJwtDecodedVerifiableCredential).compactSdJwtVc,
83
+ defaultGenerateDigest,
84
+ )
85
+
86
+ const hashAlg = decodedSdJwt.signedPayload._sd_alg ?? 'sha-256'
87
+ const sdHash = calculateSdHash(decodedSdJwt.compactSdJwtVc, hashAlg, defaultGenerateDigest)
88
+ const kbJwtPayload: PartialSdJwtKbJwt['payload'] = {
89
+ iat: Math.floor(Date.now() / 1000 - CLOCK_SKEW),
90
+ sd_hash: sdHash,
91
+ }
92
+
93
+ const presentationResult = await agent.createSdJwtPresentation({
94
+ presentation: decodedSdJwt.compactSdJwtVc,
95
+ kb: {
96
+ payload: kbJwtPayload as any, // FIXME? (typescript seems impossible)
97
+ },
98
+ })
99
+
100
+ return {
101
+ documentFormat: DocumentFormat.SD_JWT_VC,
102
+ presentationPayload: presentationResult.presentation,
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Creates a JSON-LD presentation with proof
108
+ */
109
+ async function createJsonLdPresentation(
48
110
  holderDid: string,
49
- credential: UniqueDigitalCredential,
111
+ originalCredential: OriginalVerifiableCredential,
50
112
  agent: RequiredContext['agent'],
51
113
  ): Promise<LinkedVPPresentation> {
52
- logger.debug(`Creating LinkedVP presentation for ${holderDid} of credential ${credential.id}`)
114
+ // JSON-LD VC - create JSON-LD VP with challenge and domain in proof
115
+ const vcObject = typeof originalCredential === 'string' ? JSON.parse(originalCredential) : originalCredential
116
+
117
+ const vpObject = {
118
+ '@context': ['https://www.w3.org/2018/credentials/v1'],
119
+ type: ['VerifiablePresentation'],
120
+ verifiableCredential: [vcObject],
121
+ holder: holderDid,
122
+ }
53
123
 
54
124
  const identifier = await agent.identifierManagedGet({ identifier: holderDid })
55
- const originalCredential = extractOriginalCredential(credential)
56
- const documentFormat = CredentialMapper.detectDocumentType(originalCredential)
57
- switch (documentFormat) {
58
- case DocumentFormat.SD_JWT_VC: {
59
- // SD-JWT with KB-JWT
60
- const decodedSdJwt = await CredentialMapper.decodeSdJwtVcAsync(
61
- typeof originalCredential === 'string' ? originalCredential : (originalCredential as SdJwtDecodedVerifiableCredential).compactSdJwtVc,
62
- defaultGenerateDigest,
63
- )
64
-
65
- const hashAlg = decodedSdJwt.signedPayload._sd_alg ?? 'sha-256'
66
- const sdHash = calculateSdHash(decodedSdJwt.compactSdJwtVc, hashAlg, defaultGenerateDigest)
67
- const kbJwtPayload: PartialSdJwtKbJwt['payload'] = {
68
- iat: Math.floor(Date.now() / 1000 - CLOCK_SKEW),
69
- sd_hash: sdHash,
70
- }
71
-
72
- const presentationResult = await agent.createSdJwtPresentation({
73
- presentation: decodedSdJwt.compactSdJwtVc,
74
- kb: {
75
- payload: kbJwtPayload as any, // FIXME?
76
- },
77
- })
78
-
79
- return {
80
- documentFormat,
81
- presentationPayload: presentationResult.presentation,
82
- }
83
- }
84
- case DocumentFormat.JSONLD: {
85
- // JSON-LD VC - create JSON-LD VP with challenge and domain in proof
86
- const vcObject = typeof originalCredential === 'string' ? JSON.parse(originalCredential) : originalCredential
87
-
88
- const vpObject = {
89
- '@context': ['https://www.w3.org/2018/credentials/v1'],
90
- type: ['VerifiablePresentation'],
91
- verifiableCredential: [vcObject],
92
- holder: holderDid,
93
- }
94
-
95
- // Create JSON-LD VP with proof
96
- const verifiablePresentationSP = await agent.createVerifiablePresentation({
97
- presentation: vpObject,
98
- proofFormat: 'lds',
99
- keyRef: identifier.kmsKeyRef || identifier.kid,
100
- })
101
- return {
102
- documentFormat,
103
- presentationPayload: verifiablePresentationSP,
104
- }
105
- }
106
- case DocumentFormat.MSO_MDOC: {
107
- // ISO mdoc - create mdoc VP token
108
- // This is a placeholder implementation
109
- // Full implementation would require:
110
- // 1. Decode the mdoc using CredentialMapper or mdoc utilities
111
- // 2. Build proper mdoc VP token with session transcript
112
- // 3. Include nonce/audience in the session transcript
113
- logger.warning('mso_mdoc format has basic support - production use requires proper mdoc VP token implementation')
114
-
115
- return {
116
- documentFormat,
117
- presentationPayload: originalCredential,
118
- }
119
- }
120
- default: {
121
- // JWT VC - create JWT VP with nonce and aud in payload
122
- const vcJwt = typeof originalCredential === 'string' ? originalCredential : JSON.stringify(originalCredential)
123
-
124
- // Create VP JWT using agent method
125
- const vpPayload = {
126
- iss: holderDid,
127
- vp: {
128
- '@context': ['https://www.w3.org/2018/credentials/v1'],
129
- type: ['VerifiablePresentation'],
130
- holder: holderDid,
131
- verifiableCredential: [vcJwt],
132
- },
133
- iat: Math.floor(Date.now() / 1000 - CLOCK_SKEW),
134
- exp: Math.floor(Date.now() / 1000 + 600 + CLOCK_SKEW), // 10 minutes
135
- }
136
-
137
- // Use the agent's JWT creation capability
138
- const vpJwt = await agent.createVerifiablePresentation({
139
- presentation: vpPayload.vp,
140
- proofFormat: 'jwt',
141
- keyRef: identifier.kmsKeyRef || identifier.kid,
142
- })
143
-
144
- return {
145
- documentFormat,
146
- presentationPayload: (vpJwt.proof && 'jwt' in vpJwt.proof && vpJwt.proof.jwt) || vpJwt,
147
- }
148
- }
125
+
126
+ // Create JSON-LD VP with proof
127
+ const verifiablePresentationSP = await agent.createVerifiablePresentation({
128
+ presentation: vpObject,
129
+ proofFormat: 'lds',
130
+ keyRef: identifier.kmsKeyRef || identifier.kid,
131
+ })
132
+ return {
133
+ documentFormat: DocumentFormat.JSONLD,
134
+ presentationPayload: verifiablePresentationSP,
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Creates an ISO mdoc presentation (basic support)
140
+ */
141
+ async function createMdocPresentation(originalCredential: OriginalVerifiableCredential): Promise<LinkedVPPresentation> {
142
+ // ISO mdoc - create mdoc VP token
143
+ // This is a placeholder implementation
144
+ // Full implementation would require:
145
+ // 1. Decode the mdoc using CredentialMapper or mdoc utilities
146
+ // 2. Build proper mdoc VP token with session transcript
147
+ // 3. Include nonce/audience in the session transcript
148
+ logger.warning('mso_mdoc format has basic support - production use requires proper mdoc VP token implementation')
149
+
150
+ return {
151
+ documentFormat: DocumentFormat.MSO_MDOC,
152
+ presentationPayload: originalCredential,
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Creates a JWT presentation
158
+ */
159
+ async function createJwtPresentation(
160
+ holderDid: string,
161
+ originalCredential: OriginalVerifiableCredential,
162
+ agent: RequiredContext['agent'],
163
+ ): Promise<LinkedVPPresentation> {
164
+ // JWT VC - create JWT VP with nonce and aud in payload
165
+ const vcJwt = typeof originalCredential === 'string' ? originalCredential : JSON.stringify(originalCredential)
166
+
167
+ const identifier = await agent.identifierManagedGet({ identifier: holderDid })
168
+
169
+ // Create VP JWT using agent method
170
+ const vpPayload = {
171
+ iss: holderDid,
172
+ vp: {
173
+ '@context': ['https://www.w3.org/2018/credentials/v1'],
174
+ type: ['VerifiablePresentation'],
175
+ holder: holderDid,
176
+ verifiableCredential: [vcJwt],
177
+ },
178
+ iat: Math.floor(Date.now() / 1000 - CLOCK_SKEW),
179
+ exp: Math.floor(Date.now() / 1000 + 600 + CLOCK_SKEW), // 10 minutes
180
+ }
181
+
182
+ // Use the agent's JWT creation capability
183
+ const vpJwt = await agent.createVerifiablePresentation({
184
+ presentation: vpPayload.vp,
185
+ proofFormat: 'jwt',
186
+ keyRef: identifier.kmsKeyRef || identifier.kid,
187
+ })
188
+
189
+ return {
190
+ documentFormat: DocumentFormat.JWT,
191
+ presentationPayload: (vpJwt.proof && 'jwt' in vpJwt.proof && vpJwt.proof.jwt) || vpJwt,
149
192
  }
150
193
  }
@@ -55,6 +55,8 @@ export interface ILinkedVPManager extends IPluginMethodMap {
55
55
  export type PublishCredentialArgs = {
56
56
  digitalCredentialId: string
57
57
  linkedVpId?: string // Optional: if not provided, will be auto-generated
58
+ linkedVpFrom?: Date
59
+ linkedVpUntil?: Date
58
60
  }
59
61
 
60
62
  export type UnpublishCredentialArgs = {
@@ -66,17 +68,20 @@ export type HasLinkedVPEntryArgs = {
66
68
  }
67
69
 
68
70
  export type GetServiceEntriesArgs = {
71
+ subjectDid?: string
69
72
  tenantId?: string
70
73
  }
71
74
 
72
75
  export type GeneratePresentationArgs = {
73
76
  linkedVpId: string
77
+ linkedVpUntil?: Date
74
78
  }
75
79
 
76
80
  export type LinkedVPEntry = {
77
81
  id: string
78
82
  linkedVpId: string
79
83
  linkedVpFrom?: Date
84
+ linkedVpUntil?: Date
80
85
  tenantId?: string
81
86
  createdAt: Date
82
87
  }