@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.
- package/dist/index.cjs +120 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +120 -92
- package/dist/index.js.map +1 -1
- package/package.json +15 -15
- package/src/__tests__/shared/linkedVPManagerAgentLogic.ts +1 -1
- package/src/agent/LinkedVPManager.ts +17 -7
- package/src/services/LinkedVPService.ts +143 -100
- package/src/types/ILinkedVPManager.ts +5 -0
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
111
|
+
originalCredential: OriginalVerifiableCredential,
|
|
50
112
|
agent: RequiredContext['agent'],
|
|
51
113
|
): Promise<LinkedVPPresentation> {
|
|
52
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
}
|