@sphereon/ssi-sdk.siopv2-oid4vp-rp-auth 0.34.1-fix.80 → 0.34.1-next.278
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 +481 -429
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +739 -67
- package/dist/index.d.ts +739 -67
- package/dist/index.js +472 -421
- package/dist/index.js.map +1 -1
- package/package.json +18 -18
- package/src/RPInstance.ts +14 -29
- package/src/agent/SIOPv2RP.ts +138 -66
- package/src/functions.ts +49 -52
- package/src/index.ts +1 -1
- package/src/types/ISIOPv2RP.ts +32 -59
package/src/agent/SIOPv2RP.ts
CHANGED
|
@@ -3,10 +3,15 @@ import {
|
|
|
3
3
|
AuthorizationResponsePayload,
|
|
4
4
|
AuthorizationResponseState,
|
|
5
5
|
AuthorizationResponseStateStatus,
|
|
6
|
+
AuthorizationResponseStateWithVerifiedData,
|
|
6
7
|
decodeUriAsJson,
|
|
8
|
+
EncodedDcqlPresentationVpToken,
|
|
7
9
|
VerifiedAuthorizationResponse,
|
|
8
10
|
} from '@sphereon/did-auth-siop'
|
|
9
11
|
import { getAgentResolver } from '@sphereon/ssi-sdk-ext.did-utils'
|
|
12
|
+
import { shaHasher as defaultHasher } from '@sphereon/ssi-sdk.core'
|
|
13
|
+
import { validate as isValidUUID } from 'uuid'
|
|
14
|
+
import type { ImportDcqlQueryItem } from '@sphereon/ssi-sdk.pd-manager'
|
|
10
15
|
import {
|
|
11
16
|
AdditionalClaims,
|
|
12
17
|
CredentialMapper,
|
|
@@ -22,8 +27,8 @@ import {
|
|
|
22
27
|
SdJwtDecodedVerifiableCredential,
|
|
23
28
|
} from '@sphereon/ssi-types'
|
|
24
29
|
import { IAgentPlugin } from '@veramo/core'
|
|
30
|
+
import { DcqlQuery } from 'dcql'
|
|
25
31
|
import {
|
|
26
|
-
AuthorizationResponseStateWithVerifiedData,
|
|
27
32
|
IAuthorizationRequestPayloads,
|
|
28
33
|
ICreateAuthRequestArgs,
|
|
29
34
|
IGetAuthRequestStateArgs,
|
|
@@ -39,13 +44,9 @@ import {
|
|
|
39
44
|
IUpdateRequestStateArgs,
|
|
40
45
|
IVerifyAuthResponseStateArgs,
|
|
41
46
|
schema,
|
|
42
|
-
VerifiedDataMode,
|
|
43
47
|
} from '../index'
|
|
44
48
|
import { RPInstance } from '../RPInstance'
|
|
45
|
-
|
|
46
49
|
import { ISIOPv2RP } from '../types/ISIOPv2RP'
|
|
47
|
-
import { shaHasher as defaultHasher } from '@sphereon/ssi-sdk.core'
|
|
48
|
-
import { DcqlQuery } from 'dcql'
|
|
49
50
|
|
|
50
51
|
export class SIOPv2RP implements IAgentPlugin {
|
|
51
52
|
private readonly opts: ISiopv2RPOpts
|
|
@@ -85,7 +86,14 @@ export class SIOPv2RP implements IAgentPlugin {
|
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
private async createAuthorizationRequestURI(createArgs: ICreateAuthRequestArgs, context: IRequiredContext): Promise<string> {
|
|
88
|
-
return await this.getRPInstance(
|
|
89
|
+
return await this.getRPInstance(
|
|
90
|
+
{
|
|
91
|
+
createWhenNotPresent: true,
|
|
92
|
+
responseRedirectURI: createArgs.responseRedirectURI,
|
|
93
|
+
...(createArgs.useQueryIdInstance === true && { queryId: createArgs.queryId }),
|
|
94
|
+
},
|
|
95
|
+
context,
|
|
96
|
+
)
|
|
89
97
|
.then((rp) => rp.createAuthorizationRequestURI(createArgs, context))
|
|
90
98
|
.then((URI) => URI.encodedUri)
|
|
91
99
|
}
|
|
@@ -94,20 +102,20 @@ export class SIOPv2RP implements IAgentPlugin {
|
|
|
94
102
|
createArgs: ICreateAuthRequestArgs,
|
|
95
103
|
context: IRequiredContext,
|
|
96
104
|
): Promise<IAuthorizationRequestPayloads> {
|
|
97
|
-
return await this.getRPInstance({
|
|
105
|
+
return await this.getRPInstance({ createWhenNotPresent: true, queryId: createArgs.queryId }, context)
|
|
98
106
|
.then((rp) => rp.createAuthorizationRequest(createArgs, context))
|
|
99
107
|
.then(async (request) => {
|
|
100
108
|
const authRequest: IAuthorizationRequestPayloads = {
|
|
101
109
|
authorizationRequest: request.payload,
|
|
102
110
|
requestObject: await request.requestObjectJwt(),
|
|
103
|
-
requestObjectDecoded:
|
|
111
|
+
requestObjectDecoded: request.requestObject?.getPayload(),
|
|
104
112
|
}
|
|
105
113
|
return authRequest
|
|
106
114
|
})
|
|
107
115
|
}
|
|
108
116
|
|
|
109
117
|
private async siopGetRequestState(args: IGetAuthRequestStateArgs, context: IRequiredContext): Promise<AuthorizationRequestState | undefined> {
|
|
110
|
-
return await this.getRPInstance({
|
|
118
|
+
return await this.getRPInstance({ createWhenNotPresent: false, queryId: args.queryId }, context).then((rp) =>
|
|
111
119
|
rp.get(context).then((rp) => rp.sessionManager.getRequestStateByCorrelationId(args.correlationId, args.errorOnNotFound)),
|
|
112
120
|
)
|
|
113
121
|
}
|
|
@@ -116,7 +124,7 @@ export class SIOPv2RP implements IAgentPlugin {
|
|
|
116
124
|
args: IGetAuthResponseStateArgs,
|
|
117
125
|
context: IRequiredContext,
|
|
118
126
|
): Promise<AuthorizationResponseStateWithVerifiedData | undefined> {
|
|
119
|
-
const rpInstance: RPInstance = await this.getRPInstance({
|
|
127
|
+
const rpInstance: RPInstance = await this.getRPInstance({ createWhenNotPresent: false, queryId: args.queryId }, context)
|
|
120
128
|
const authorizationResponseState: AuthorizationResponseState | undefined = await rpInstance
|
|
121
129
|
.get(context)
|
|
122
130
|
.then((rp) => rp.sessionManager.getResponseStateByCorrelationId(args.correlationId, args.errorOnNotFound))
|
|
@@ -125,11 +133,7 @@ export class SIOPv2RP implements IAgentPlugin {
|
|
|
125
133
|
}
|
|
126
134
|
|
|
127
135
|
const responseState = authorizationResponseState as AuthorizationResponseStateWithVerifiedData
|
|
128
|
-
if (
|
|
129
|
-
responseState.status === AuthorizationResponseStateStatus.VERIFIED &&
|
|
130
|
-
args.includeVerifiedData &&
|
|
131
|
-
args.includeVerifiedData !== VerifiedDataMode.NONE
|
|
132
|
-
) {
|
|
136
|
+
if (responseState.status === AuthorizationResponseStateStatus.VERIFIED) {
|
|
133
137
|
let hasher: HasherSync | undefined
|
|
134
138
|
if (
|
|
135
139
|
CredentialMapper.isSdJwtEncoded(responseState.response.payload.vp_token as OriginalVerifiablePresentation) &&
|
|
@@ -137,19 +141,36 @@ export class SIOPv2RP implements IAgentPlugin {
|
|
|
137
141
|
) {
|
|
138
142
|
hasher = defaultHasher
|
|
139
143
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
144
|
+
|
|
145
|
+
// FIXME SSISDK-64 currently assuming that all vp tokens are or type EncodedDcqlPresentationVpToken as we only work with DCQL now. But the types still indicate it can be another type of vp token
|
|
146
|
+
const vpToken = responseState.response.payload.vp_token && JSON.parse(responseState.response.payload.vp_token as EncodedDcqlPresentationVpToken)
|
|
147
|
+
const claims = []
|
|
148
|
+
for (const [credentialQueryId, presentationValue] of Object.entries(vpToken)) {
|
|
149
|
+
let singleVP: OriginalVerifiablePresentation
|
|
150
|
+
if (Array.isArray(presentationValue)) {
|
|
151
|
+
if (presentationValue.length === 0) {
|
|
152
|
+
throw Error(`DCQL query '${credentialQueryId}' has empty array of presentations`)
|
|
153
|
+
}
|
|
154
|
+
if (presentationValue.length > 1) {
|
|
155
|
+
throw Error(`DCQL query '${credentialQueryId}' has multiple presentations (${presentationValue.length}), but only one is supported atm`)
|
|
156
|
+
}
|
|
157
|
+
singleVP = presentationValue[0] as OriginalVerifiablePresentation
|
|
158
|
+
} else {
|
|
159
|
+
singleVP = presentationValue as OriginalVerifiablePresentation
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// todo this should also include mdl-mdoc
|
|
163
|
+
const presentationDecoded = CredentialMapper.decodeVerifiablePresentation(
|
|
164
|
+
singleVP as OriginalVerifiablePresentation,
|
|
165
|
+
//todo: later we want to conditionally pass in options for mdl-mdoc here
|
|
166
|
+
hasher,
|
|
167
|
+
)
|
|
168
|
+
console.log(`presentationDecoded: ${JSON.stringify(presentationDecoded)}`)
|
|
169
|
+
|
|
170
|
+
const allClaims: AdditionalClaims = {}
|
|
171
|
+
const presentationOrClaims = this.presentationOrClaimsFrom(presentationDecoded)
|
|
172
|
+
if ('verifiableCredential' in presentationOrClaims) {
|
|
173
|
+
for (const credential of presentationOrClaims.verifiableCredential) {
|
|
153
174
|
const vc = credential as IVerifiableCredential
|
|
154
175
|
const schemaValidationResult = await context.agent.cvVerifySchema({
|
|
155
176
|
credential,
|
|
@@ -172,11 +193,35 @@ export class SIOPv2RP implements IAgentPlugin {
|
|
|
172
193
|
allClaims[key] = value
|
|
173
194
|
}
|
|
174
195
|
})
|
|
196
|
+
|
|
197
|
+
claims.push({
|
|
198
|
+
id: credentialQueryId,
|
|
199
|
+
type: vc.type[0],
|
|
200
|
+
claims: allClaims,
|
|
201
|
+
})
|
|
175
202
|
}
|
|
176
|
-
|
|
177
|
-
|
|
203
|
+
} else {
|
|
204
|
+
claims.push({
|
|
205
|
+
id: credentialQueryId,
|
|
206
|
+
type: (presentationDecoded as SdJwtDecodedVerifiableCredential).decodedPayload.vct,
|
|
207
|
+
claims: presentationOrClaims,
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
responseState.verifiedData = {
|
|
213
|
+
...(responseState.response.payload.vp_token && {
|
|
214
|
+
authorization_response: {
|
|
215
|
+
vp_token:
|
|
216
|
+
typeof responseState.response.payload.vp_token === 'string'
|
|
217
|
+
? JSON.parse(responseState.response.payload.vp_token)
|
|
218
|
+
: responseState.response.payload.vp_token,
|
|
219
|
+
},
|
|
220
|
+
}),
|
|
221
|
+
...(claims.length > 0 && { credential_claims: claims }),
|
|
178
222
|
}
|
|
179
223
|
}
|
|
224
|
+
|
|
180
225
|
return responseState
|
|
181
226
|
}
|
|
182
227
|
|
|
@@ -187,16 +232,17 @@ export class SIOPv2RP implements IAgentPlugin {
|
|
|
187
232
|
| SdJwtDecodedVerifiableCredential
|
|
188
233
|
| MdocOid4vpMdocVpToken
|
|
189
234
|
| MdocDeviceResponse,
|
|
190
|
-
): AdditionalClaims | IPresentation =>
|
|
191
|
-
CredentialMapper.isSdJwtDecodedCredential(presentationDecoded)
|
|
235
|
+
): AdditionalClaims | IPresentation => {
|
|
236
|
+
return CredentialMapper.isSdJwtDecodedCredential(presentationDecoded)
|
|
192
237
|
? presentationDecoded.decodedPayload
|
|
193
238
|
: CredentialMapper.toUniformPresentation(presentationDecoded as OriginalVerifiablePresentation)
|
|
239
|
+
}
|
|
194
240
|
|
|
195
241
|
private async siopUpdateRequestState(args: IUpdateRequestStateArgs, context: IRequiredContext): Promise<AuthorizationRequestState> {
|
|
196
|
-
if (args.state !== '
|
|
197
|
-
throw Error(`Only '
|
|
242
|
+
if (args.state !== 'authorization_request_created') {
|
|
243
|
+
throw Error(`Only 'authorization_request_created' status is supported for this method at this point`)
|
|
198
244
|
}
|
|
199
|
-
return await this.getRPInstance({
|
|
245
|
+
return await this.getRPInstance({ createWhenNotPresent: false, queryId: args.queryId }, context)
|
|
200
246
|
// todo: In the SIOP library we need to update the signal method to be more like this method
|
|
201
247
|
.then((rp) =>
|
|
202
248
|
rp.get(context).then(async (rp) => {
|
|
@@ -210,7 +256,7 @@ export class SIOPv2RP implements IAgentPlugin {
|
|
|
210
256
|
}
|
|
211
257
|
|
|
212
258
|
private async siopDeleteState(args: IGetAuthResponseStateArgs, context: IRequiredContext): Promise<boolean> {
|
|
213
|
-
return await this.getRPInstance({
|
|
259
|
+
return await this.getRPInstance({ createWhenNotPresent: false, queryId: args.queryId }, context)
|
|
214
260
|
.then((rp) => rp.get(context).then((rp) => rp.sessionManager.deleteStateForCorrelationId(args.correlationId)))
|
|
215
261
|
.then(() => true)
|
|
216
262
|
}
|
|
@@ -223,11 +269,11 @@ export class SIOPv2RP implements IAgentPlugin {
|
|
|
223
269
|
typeof args.authorizationResponse === 'string'
|
|
224
270
|
? (decodeUriAsJson(args.authorizationResponse) as AuthorizationResponsePayload)
|
|
225
271
|
: args.authorizationResponse
|
|
226
|
-
return await this.getRPInstance({
|
|
272
|
+
return await this.getRPInstance({ createWhenNotPresent: false, queryId: args.queryId }, context).then((rp) =>
|
|
227
273
|
rp.get(context).then((rp) =>
|
|
228
274
|
rp.verifyAuthorizationResponse(authResponse, {
|
|
229
275
|
correlationId: args.correlationId,
|
|
230
|
-
...(args.dcqlQuery
|
|
276
|
+
...(args.dcqlQuery && { dcqlQuery: args.dcqlQuery }),
|
|
231
277
|
audience: args.audience,
|
|
232
278
|
}),
|
|
233
279
|
),
|
|
@@ -235,19 +281,18 @@ export class SIOPv2RP implements IAgentPlugin {
|
|
|
235
281
|
}
|
|
236
282
|
|
|
237
283
|
private async siopImportDefinitions(args: ImportDefinitionsArgs, context: IRequiredContext): Promise<void> {
|
|
238
|
-
const {
|
|
284
|
+
const { importItems, tenantId, version, versionControlMode } = args
|
|
239
285
|
await Promise.all(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
286
|
+
importItems.map(async (importItem: ImportDcqlQueryItem) => {
|
|
287
|
+
DcqlQuery.validate(importItem.query)
|
|
288
|
+
console.log(`persisting DCQL definition ${importItem.queryId} with versionControlMode ${versionControlMode}`)
|
|
243
289
|
|
|
244
|
-
console.log(`persisting definition ${definitionPayload.id} / ${definitionPayload.name} with versionControlMode ${versionControlMode}`)
|
|
245
290
|
return context.agent.pdmPersistDefinition({
|
|
246
291
|
definitionItem: {
|
|
292
|
+
queryId: importItem.queryId!,
|
|
247
293
|
tenantId: tenantId,
|
|
248
294
|
version: version,
|
|
249
|
-
|
|
250
|
-
dcqlPayload: definitionPair.dcqlPayload,
|
|
295
|
+
query: importItem.query,
|
|
251
296
|
},
|
|
252
297
|
opts: { versionControlMode: versionControlMode },
|
|
253
298
|
})
|
|
@@ -256,12 +301,12 @@ export class SIOPv2RP implements IAgentPlugin {
|
|
|
256
301
|
}
|
|
257
302
|
|
|
258
303
|
private async siopGetRedirectURI(args: IGetRedirectUriArgs, context: IRequiredContext): Promise<string | undefined> {
|
|
259
|
-
const instanceId = args.
|
|
304
|
+
const instanceId = args.queryId ?? SIOPv2RP._DEFAULT_OPTS_KEY
|
|
260
305
|
if (this.instances.has(instanceId)) {
|
|
261
306
|
const rpInstance = this.instances.get(instanceId)
|
|
262
307
|
if (rpInstance !== undefined) {
|
|
263
308
|
const rp = await rpInstance.get(context)
|
|
264
|
-
return rp.getResponseRedirectUri({
|
|
309
|
+
return await rp.getResponseRedirectUri({
|
|
265
310
|
correlation_id: args.correlationId,
|
|
266
311
|
correlationId: args.correlationId,
|
|
267
312
|
...(args.state && { state: args.state }),
|
|
@@ -271,37 +316,64 @@ export class SIOPv2RP implements IAgentPlugin {
|
|
|
271
316
|
return undefined
|
|
272
317
|
}
|
|
273
318
|
|
|
274
|
-
async getRPInstance({
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
319
|
+
async getRPInstance({ createWhenNotPresent, queryId, responseRedirectURI }: ISiopRPInstanceArgs, context: IRequiredContext): Promise<RPInstance> {
|
|
320
|
+
let rpInstanceId: string = SIOPv2RP._DEFAULT_OPTS_KEY
|
|
321
|
+
let rpInstance: RPInstance | undefined
|
|
322
|
+
if (queryId) {
|
|
323
|
+
if (this.instances.has(queryId)) {
|
|
324
|
+
rpInstanceId = queryId
|
|
325
|
+
rpInstance = this.instances.get(rpInstanceId)!
|
|
326
|
+
} else if (isValidUUID(queryId)) {
|
|
327
|
+
try {
|
|
328
|
+
// Check whether queryId is actually the PD item id
|
|
329
|
+
const pd = await context.agent.pdmGetDefinition({ itemId: queryId })
|
|
330
|
+
if (this.instances.has(pd.queryId)) {
|
|
331
|
+
rpInstanceId = pd.queryId
|
|
332
|
+
rpInstance = this.instances.get(rpInstanceId)!
|
|
333
|
+
}
|
|
334
|
+
} catch (ignore) {}
|
|
335
|
+
}
|
|
336
|
+
if (createWhenNotPresent) {
|
|
337
|
+
rpInstanceId = queryId
|
|
338
|
+
} else {
|
|
339
|
+
rpInstance = this.instances.get(rpInstanceId)
|
|
340
|
+
}
|
|
341
|
+
} else {
|
|
342
|
+
rpInstance = this.instances.get(rpInstanceId)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (!rpInstance) {
|
|
346
|
+
if (!createWhenNotPresent) {
|
|
347
|
+
return Promise.reject(`No RP instance found for key ${rpInstanceId}`)
|
|
348
|
+
}
|
|
349
|
+
const instanceOpts = this.getInstanceOpts(queryId)
|
|
350
|
+
const rpOpts = await this.getRPOptions(context, { queryId, responseRedirectURI: responseRedirectURI })
|
|
279
351
|
if (!rpOpts.identifierOpts.resolveOpts?.resolver || typeof rpOpts.identifierOpts.resolveOpts.resolver.resolve !== 'function') {
|
|
280
352
|
if (!rpOpts.identifierOpts?.resolveOpts) {
|
|
281
353
|
rpOpts.identifierOpts = { ...rpOpts.identifierOpts }
|
|
282
354
|
rpOpts.identifierOpts.resolveOpts = { ...rpOpts.identifierOpts.resolveOpts }
|
|
283
355
|
}
|
|
284
|
-
console.log('Using agent DID resolver for RP instance with definition id ' +
|
|
356
|
+
console.log('Using agent DID resolver for RP instance with definition id ' + queryId)
|
|
285
357
|
rpOpts.identifierOpts.resolveOpts.resolver = getAgentResolver(context, {
|
|
286
358
|
uniresolverResolution: true,
|
|
287
359
|
localResolution: true,
|
|
288
360
|
resolverResolution: true,
|
|
289
361
|
})
|
|
290
362
|
}
|
|
291
|
-
|
|
363
|
+
rpInstance = new RPInstance({ rpOpts, pexOpts: instanceOpts })
|
|
364
|
+
this.instances.set(rpInstanceId, rpInstance)
|
|
292
365
|
}
|
|
293
|
-
const rpInstance = this.instances.get(instanceId)!
|
|
294
366
|
if (responseRedirectURI) {
|
|
295
367
|
rpInstance.rpOptions.responseRedirectUri = responseRedirectURI
|
|
296
368
|
}
|
|
297
369
|
return rpInstance
|
|
298
370
|
}
|
|
299
371
|
|
|
300
|
-
async getRPOptions(context: IRequiredContext, opts: {
|
|
301
|
-
const {
|
|
302
|
-
const options = this.getInstanceOpts(
|
|
372
|
+
async getRPOptions(context: IRequiredContext, opts: { queryId?: string; responseRedirectURI?: string }): Promise<IRPOptions> {
|
|
373
|
+
const { queryId, responseRedirectURI: responseRedirectURI } = opts
|
|
374
|
+
const options = this.getInstanceOpts(queryId)?.rpOpts ?? this.opts.defaultOpts
|
|
303
375
|
if (!options) {
|
|
304
|
-
throw Error(`Could not get specific nor default options for definition ${
|
|
376
|
+
throw Error(`Could not get specific nor default options for definition ${queryId}`)
|
|
305
377
|
}
|
|
306
378
|
if (this.opts.defaultOpts) {
|
|
307
379
|
if (!options.identifierOpts) {
|
|
@@ -332,22 +404,22 @@ export class SIOPv2RP implements IAgentPlugin {
|
|
|
332
404
|
return options
|
|
333
405
|
}
|
|
334
406
|
|
|
335
|
-
getInstanceOpts(
|
|
407
|
+
getInstanceOpts(queryId?: string): IPEXInstanceOptions | undefined {
|
|
336
408
|
if (!this.opts.instanceOpts) return undefined
|
|
337
409
|
|
|
338
|
-
const instanceOpt =
|
|
410
|
+
const instanceOpt = queryId ? this.opts.instanceOpts.find((i) => i.queryId === queryId) : undefined
|
|
339
411
|
|
|
340
|
-
return instanceOpt ?? this.getDefaultOptions(
|
|
412
|
+
return instanceOpt ?? this.getDefaultOptions(queryId)
|
|
341
413
|
}
|
|
342
414
|
|
|
343
|
-
private getDefaultOptions(
|
|
415
|
+
private getDefaultOptions(queryId: string | undefined) {
|
|
344
416
|
if (!this.opts.instanceOpts) return undefined
|
|
345
417
|
|
|
346
|
-
const defaultOptions = this.opts.instanceOpts.find((i) => i.
|
|
418
|
+
const defaultOptions = this.opts.instanceOpts.find((i) => i.queryId === 'default')
|
|
347
419
|
if (defaultOptions) {
|
|
348
420
|
const clonedOptions = { ...defaultOptions }
|
|
349
|
-
if (
|
|
350
|
-
clonedOptions.
|
|
421
|
+
if (queryId !== undefined) {
|
|
422
|
+
clonedOptions.queryId = queryId
|
|
351
423
|
}
|
|
352
424
|
return clonedOptions
|
|
353
425
|
}
|
package/src/functions.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
ClientIdentifierPrefix,
|
|
3
3
|
ClientMetadataOpts,
|
|
4
|
+
DcqlQueryLookupCallback,
|
|
4
5
|
InMemoryRPSessionManager,
|
|
5
6
|
PassBy,
|
|
6
7
|
PresentationVerificationCallback,
|
|
@@ -28,14 +29,14 @@ import {
|
|
|
28
29
|
} from '@sphereon/ssi-sdk-ext.identifier-resolution'
|
|
29
30
|
import { JwtCompactResult } from '@sphereon/ssi-sdk-ext.jwt-service'
|
|
30
31
|
import { IVerifySdJwtPresentationResult } from '@sphereon/ssi-sdk.sd-jwt'
|
|
31
|
-
import { CredentialMapper,
|
|
32
|
+
import { CredentialMapper, HasherSync, OriginalVerifiableCredential, PresentationSubmission } from '@sphereon/ssi-types'
|
|
32
33
|
import { IVerifyCallbackArgs, IVerifyCredentialResult, VerifyCallback } from '@sphereon/wellknown-dids-client'
|
|
33
|
-
// import { KeyAlgo, SuppliedSigner } from '@sphereon/ssi-sdk.core'
|
|
34
34
|
import { TKeyType } from '@veramo/core'
|
|
35
35
|
import { JWTVerifyOptions } from 'did-jwt'
|
|
36
36
|
import { Resolvable } from 'did-resolver'
|
|
37
37
|
import { EventEmitter } from 'events'
|
|
38
|
-
import {
|
|
38
|
+
import { validate as isValidUUID } from 'uuid'
|
|
39
|
+
import { IRequiredContext, IRPOptions, ISIOPIdentifierOptions } from './types/ISIOPv2RP'
|
|
39
40
|
import { DcqlQuery } from 'dcql'
|
|
40
41
|
import { defaultHasher } from '@sphereon/ssi-sdk.core'
|
|
41
42
|
|
|
@@ -43,7 +44,7 @@ export function getRequestVersion(rpOptions: IRPOptions): SupportedVersion {
|
|
|
43
44
|
if (Array.isArray(rpOptions.supportedVersions) && rpOptions.supportedVersions.length > 0) {
|
|
44
45
|
return rpOptions.supportedVersions[0]
|
|
45
46
|
}
|
|
46
|
-
return SupportedVersion.
|
|
47
|
+
return SupportedVersion.OID4VP_v1
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
function getWellKnownDIDVerifyCallback(siopIdentifierOpts: ISIOPIdentifierOptions, context: IRequiredContext) {
|
|
@@ -58,6 +59,29 @@ function getWellKnownDIDVerifyCallback(siopIdentifierOpts: ISIOPIdentifierOption
|
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
export function getDcqlQueryLookupCallback(context: IRequiredContext): DcqlQueryLookupCallback {
|
|
63
|
+
async function dcqlQueryLookup(queryId: string, version?: string, tenantId?: string): Promise<DcqlQuery> {
|
|
64
|
+
// TODO Add caching?
|
|
65
|
+
const result = await context.agent.pdmGetDefinitions({
|
|
66
|
+
filter: [
|
|
67
|
+
{
|
|
68
|
+
queryId,
|
|
69
|
+
...(tenantId && { tenantId }),
|
|
70
|
+
...(version && { version }),
|
|
71
|
+
},
|
|
72
|
+
...(isValidUUID(queryId) ? [{ id: queryId }] : []),
|
|
73
|
+
],
|
|
74
|
+
})
|
|
75
|
+
if (result && result.length > 0) {
|
|
76
|
+
return result[0].query
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return Promise.reject(Error(`No dcql query found for queryId ${queryId}`))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return dcqlQueryLookup
|
|
83
|
+
}
|
|
84
|
+
|
|
61
85
|
export function getPresentationVerificationCallback(
|
|
62
86
|
idOpts: ManagedIdentifierOptsOrResult,
|
|
63
87
|
context: IRequiredContext,
|
|
@@ -69,7 +93,6 @@ export function getPresentationVerificationCallback(
|
|
|
69
93
|
if (CredentialMapper.isSdJwtEncoded(args)) {
|
|
70
94
|
const result: IVerifySdJwtPresentationResult = await context.agent.verifySdJwtPresentation({
|
|
71
95
|
presentation: args,
|
|
72
|
-
kb: true,
|
|
73
96
|
})
|
|
74
97
|
// fixme: investigate the correct way to handle this
|
|
75
98
|
return { verified: !!result.payload }
|
|
@@ -103,35 +126,11 @@ export function getPresentationVerificationCallback(
|
|
|
103
126
|
|
|
104
127
|
export async function createRPBuilder(args: {
|
|
105
128
|
rpOpts: IRPOptions
|
|
106
|
-
pexOpts?: IPEXOptions | undefined
|
|
107
129
|
definition?: IPresentationDefinition
|
|
108
|
-
dcql?: DcqlQuery
|
|
109
130
|
context: IRequiredContext
|
|
110
131
|
}): Promise<RPBuilder> {
|
|
111
|
-
const { rpOpts,
|
|
132
|
+
const { rpOpts, context } = args
|
|
112
133
|
const { identifierOpts } = rpOpts
|
|
113
|
-
let definition: IPresentationDefinition | undefined = args.definition
|
|
114
|
-
let dcqlQuery: DcqlQuery | undefined = args.dcql
|
|
115
|
-
|
|
116
|
-
if (!definition && pexOpts && pexOpts.definitionId) {
|
|
117
|
-
const presentationDefinitionItems = await context.agent.pdmGetDefinitions({
|
|
118
|
-
filter: [
|
|
119
|
-
{
|
|
120
|
-
definitionId: pexOpts.definitionId,
|
|
121
|
-
version: pexOpts.version,
|
|
122
|
-
tenantId: pexOpts.tenantId,
|
|
123
|
-
},
|
|
124
|
-
],
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
if (presentationDefinitionItems.length > 0) {
|
|
128
|
-
const presentationDefinitionItem = presentationDefinitionItems[0]
|
|
129
|
-
definition = presentationDefinitionItem.definitionPayload
|
|
130
|
-
if (!dcqlQuery && presentationDefinitionItem.dcqlPayload) {
|
|
131
|
-
dcqlQuery = presentationDefinitionItem.dcqlPayload as DcqlQuery // cast from DcqlQueryREST back to valibot DcqlQuery
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
134
|
|
|
136
135
|
const didMethods = identifierOpts.supportedDIDMethods ?? (await getAgentDIDMethods(context))
|
|
137
136
|
const eventEmitter = rpOpts.eventEmitter ?? new EventEmitter()
|
|
@@ -161,7 +160,7 @@ export async function createRPBuilder(args: {
|
|
|
161
160
|
uniresolverResolution: rpOpts.identifierOpts.resolveOpts?.noUniversalResolverFallback !== true,
|
|
162
161
|
})
|
|
163
162
|
//todo: probably wise to first look and see if we actually need the hasher to begin with
|
|
164
|
-
let hasher:
|
|
163
|
+
let hasher: HasherSync | undefined = rpOpts.credentialOpts?.hasher
|
|
165
164
|
if (!rpOpts.credentialOpts?.hasher || typeof rpOpts.credentialOpts?.hasher !== 'function') {
|
|
166
165
|
hasher = defaultHasher
|
|
167
166
|
}
|
|
@@ -171,9 +170,7 @@ export async function createRPBuilder(args: {
|
|
|
171
170
|
.withResponseMode(rpOpts.responseMode ?? ResponseMode.POST)
|
|
172
171
|
.withResponseType(ResponseType.VP_TOKEN, PropertyTarget.REQUEST_OBJECT)
|
|
173
172
|
// todo: move to options fill/correct method
|
|
174
|
-
.withSupportedVersions(
|
|
175
|
-
rpOpts.supportedVersions ?? [SupportedVersion.JWT_VC_PRESENTATION_PROFILE_v1, SupportedVersion.SIOPv2_ID1, SupportedVersion.SIOPv2_D11],
|
|
176
|
-
)
|
|
173
|
+
.withSupportedVersions(rpOpts.supportedVersions ?? [SupportedVersion.OID4VP_v1, SupportedVersion.SIOPv2_OID4VP_D28])
|
|
177
174
|
|
|
178
175
|
.withEventEmitter(eventEmitter)
|
|
179
176
|
.withSessionManager(rpOpts.sessionManager ?? new InMemoryRPSessionManager(eventEmitter))
|
|
@@ -192,23 +189,21 @@ export async function createRPBuilder(args: {
|
|
|
192
189
|
context,
|
|
193
190
|
),
|
|
194
191
|
)
|
|
192
|
+
.withDcqlQueryLookup(getDcqlQueryLookupCallback(context))
|
|
195
193
|
.withRevocationVerification(RevocationVerification.NEVER)
|
|
196
194
|
.withPresentationVerification(getPresentationVerificationCallback(identifierOpts.idOpts, context))
|
|
197
195
|
|
|
198
196
|
const oidfOpts = identifierOpts.oidfOpts
|
|
199
197
|
if (oidfOpts && isExternalIdentifierOIDFEntityIdOpts(oidfOpts)) {
|
|
200
|
-
builder.withEntityId(oidfOpts.identifier, PropertyTarget.REQUEST_OBJECT)
|
|
198
|
+
builder.withEntityId(oidfOpts.identifier, PropertyTarget.REQUEST_OBJECT)
|
|
201
199
|
} else {
|
|
202
200
|
const resolution = await context.agent.identifierManagedGet(identifierOpts.idOpts)
|
|
203
|
-
|
|
204
|
-
.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
(resolution.clientIdScheme as ClientIdScheme) ?? (identifierOpts.idOpts.clientIdScheme as ClientIdScheme),
|
|
210
|
-
PropertyTarget.REQUEST_OBJECT,
|
|
211
|
-
)
|
|
201
|
+
const clientId: string =
|
|
202
|
+
rpOpts.clientMetadataOpts?.client_id ??
|
|
203
|
+
resolution.issuer ??
|
|
204
|
+
(isManagedIdentifierDidResult(resolution) ? resolution.did : resolution.jwkThumbprint)
|
|
205
|
+
const clientIdPrefixed = prefixClientId(clientId)
|
|
206
|
+
builder.withClientId(clientIdPrefixed, PropertyTarget.REQUEST_OBJECT)
|
|
212
207
|
}
|
|
213
208
|
|
|
214
209
|
if (hasher) {
|
|
@@ -222,13 +217,6 @@ export async function createRPBuilder(args: {
|
|
|
222
217
|
//fixme: this has been removed in the new version of did-auth-siop
|
|
223
218
|
// builder.withWellknownDIDVerifyCallback(getWellKnownDIDVerifyCallback(didOpts, context))
|
|
224
219
|
|
|
225
|
-
if (definition) {
|
|
226
|
-
builder.withPresentationDefinition({ definition }, PropertyTarget.REQUEST_OBJECT)
|
|
227
|
-
}
|
|
228
|
-
if (dcqlQuery) {
|
|
229
|
-
builder.withDcqlQuery(dcqlQuery)
|
|
230
|
-
}
|
|
231
|
-
|
|
232
220
|
if (rpOpts.responseRedirectUri) {
|
|
233
221
|
builder.withResponseRedirectUri(rpOpts.responseRedirectUri)
|
|
234
222
|
}
|
|
@@ -309,3 +297,12 @@ export function getSigningAlgo(type: TKeyType): SigningAlgo {
|
|
|
309
297
|
throw Error('Key type not yet supported')
|
|
310
298
|
}
|
|
311
299
|
}
|
|
300
|
+
|
|
301
|
+
export function prefixClientId(clientId: string): string {
|
|
302
|
+
// FIXME SSISDK-60
|
|
303
|
+
if (clientId.startsWith('did:')) {
|
|
304
|
+
return `${ClientIdentifierPrefix.DECENTRALIZED_IDENTIFIER}:${clientId}`
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return clientId
|
|
308
|
+
}
|