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