@sphereon/ssi-sdk.siopv2-oid4vp-rp-auth 0.34.1-feature.SSISDK.58.host.nonce.endpoint.194 → 0.34.1-feature.SSISDK.62.219

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.
@@ -9,6 +9,8 @@ import {
9
9
  } from '@sphereon/did-auth-siop'
10
10
  import { getAgentResolver } from '@sphereon/ssi-sdk-ext.did-utils'
11
11
  import { shaHasher as defaultHasher } from '@sphereon/ssi-sdk.core'
12
+ import { validate as isValidUUID } from 'uuid'
13
+
12
14
  import type { ImportDcqlQueryItem } from '@sphereon/ssi-sdk.pd-manager'
13
15
  import {
14
16
  AdditionalClaims,
@@ -86,7 +88,11 @@ export class SIOPv2RP implements IAgentPlugin {
86
88
 
87
89
  private async createAuthorizationRequestURI(createArgs: ICreateAuthRequestArgs, context: IRequiredContext): Promise<string> {
88
90
  return await this.getRPInstance(
89
- { responseRedirectURI: createArgs.responseRedirectURI, ...(createArgs.useQueryIdInstance === true && { queryId: createArgs.queryId } ) },
91
+ {
92
+ createWhenNotPresent: true,
93
+ responseRedirectURI: createArgs.responseRedirectURI,
94
+ ...(createArgs.useQueryIdInstance === true && { queryId: createArgs.queryId } ),
95
+ },
90
96
  context,
91
97
  )
92
98
  .then((rp) => rp.createAuthorizationRequestURI(createArgs, context))
@@ -97,7 +103,7 @@ export class SIOPv2RP implements IAgentPlugin {
97
103
  createArgs: ICreateAuthRequestArgs,
98
104
  context: IRequiredContext,
99
105
  ): Promise<IAuthorizationRequestPayloads> {
100
- return await this.getRPInstance({ queryId: createArgs.queryId }, context)
106
+ return await this.getRPInstance({ createWhenNotPresent: true, queryId: createArgs.queryId }, context)
101
107
  .then((rp) => rp.createAuthorizationRequest(createArgs, context))
102
108
  .then(async (request) => {
103
109
  const authRequest: IAuthorizationRequestPayloads = {
@@ -110,7 +116,7 @@ export class SIOPv2RP implements IAgentPlugin {
110
116
  }
111
117
 
112
118
  private async siopGetRequestState(args: IGetAuthRequestStateArgs, context: IRequiredContext): Promise<AuthorizationRequestState | undefined> {
113
- return await this.getRPInstance({ queryId: args.queryId }, context).then((rp) =>
119
+ return await this.getRPInstance({ createWhenNotPresent: false, queryId: args.queryId }, context).then((rp) =>
114
120
  rp.get(context).then((rp) =>
115
121
  rp.sessionManager.getRequestStateByCorrelationId(args.correlationId, args.errorOnNotFound)
116
122
  ),
@@ -121,7 +127,7 @@ export class SIOPv2RP implements IAgentPlugin {
121
127
  args: IGetAuthResponseStateArgs,
122
128
  context: IRequiredContext,
123
129
  ): Promise<AuthorizationResponseStateWithVerifiedData | undefined> {
124
- const rpInstance: RPInstance = await this.getRPInstance({ queryId: args.queryId }, context)
130
+ const rpInstance: RPInstance = await this.getRPInstance({ createWhenNotPresent: false, queryId: args.queryId }, context)
125
131
  const authorizationResponseState: AuthorizationResponseState | undefined = await rpInstance
126
132
  .get(context)
127
133
  .then((rp) => rp.sessionManager.getResponseStateByCorrelationId(args.correlationId, args.errorOnNotFound))
@@ -201,7 +207,7 @@ export class SIOPv2RP implements IAgentPlugin {
201
207
  if (args.state !== 'authorization_request_created') {
202
208
  throw Error(`Only 'authorization_request_created' status is supported for this method at this point`)
203
209
  }
204
- return await this.getRPInstance({ queryId: args.queryId }, context)
210
+ return await this.getRPInstance({ createWhenNotPresent: false, queryId: args.queryId }, context)
205
211
  // todo: In the SIOP library we need to update the signal method to be more like this method
206
212
  .then((rp) =>
207
213
  rp.get(context).then(async (rp) => {
@@ -215,7 +221,7 @@ export class SIOPv2RP implements IAgentPlugin {
215
221
  }
216
222
 
217
223
  private async siopDeleteState(args: IGetAuthResponseStateArgs, context: IRequiredContext): Promise<boolean> {
218
- return await this.getRPInstance({ queryId: args.queryId }, context)
224
+ return await this.getRPInstance({ createWhenNotPresent: false, queryId: args.queryId }, context)
219
225
  .then((rp) => rp.get(context).then((rp) => rp.sessionManager.deleteStateForCorrelationId(args.correlationId)))
220
226
  .then(() => true)
221
227
  }
@@ -228,7 +234,7 @@ export class SIOPv2RP implements IAgentPlugin {
228
234
  typeof args.authorizationResponse === 'string'
229
235
  ? (decodeUriAsJson(args.authorizationResponse) as AuthorizationResponsePayload)
230
236
  : args.authorizationResponse
231
- return await this.getRPInstance({ queryId: args.queryId }, context).then((rp) =>
237
+ return await this.getRPInstance({ createWhenNotPresent: false, queryId: args.queryId }, context).then((rp) =>
232
238
  rp.get(context).then((rp) =>
233
239
  rp.verifyAuthorizationResponse(authResponse, {
234
240
  correlationId: args.correlationId,
@@ -275,9 +281,36 @@ export class SIOPv2RP implements IAgentPlugin {
275
281
  return undefined
276
282
  }
277
283
 
278
- async getRPInstance({ queryId, responseRedirectURI }: ISiopRPInstanceArgs, context: IRequiredContext): Promise<RPInstance> {
279
- const instanceId = queryId ?? SIOPv2RP._DEFAULT_OPTS_KEY
280
- if (!this.instances.has(instanceId)) {
284
+ async getRPInstance({ createWhenNotPresent, queryId, responseRedirectURI }: ISiopRPInstanceArgs, context: IRequiredContext): Promise<RPInstance> {
285
+ let rpInstanceId: string = SIOPv2RP._DEFAULT_OPTS_KEY
286
+ let rpInstance: RPInstance | undefined
287
+ if (queryId) {
288
+ if (this.instances.has(queryId)) {
289
+ rpInstanceId = queryId
290
+ rpInstance = this.instances.get(rpInstanceId)!
291
+ } else if (isValidUUID(queryId)) {
292
+ try {
293
+ // Check whether queryId is actually the PD item id
294
+ const pd = await context.agent.pdmGetDefinition({ itemId: queryId })
295
+ if (this.instances.has(pd.queryId)) {
296
+ rpInstanceId = pd.queryId
297
+ rpInstance = this.instances.get(rpInstanceId)!
298
+ }
299
+ } catch (ignore) {}
300
+ }
301
+ if (createWhenNotPresent) {
302
+ rpInstanceId = queryId
303
+ } else {
304
+ rpInstance = this.instances.get(rpInstanceId)
305
+ }
306
+ } else {
307
+ rpInstance = this.instances.get(rpInstanceId)
308
+ }
309
+
310
+ if (!rpInstance) {
311
+ if (!createWhenNotPresent) {
312
+ return Promise.reject(`No RP instance found for key ${rpInstanceId}`)
313
+ }
281
314
  const instanceOpts = this.getInstanceOpts(queryId)
282
315
  const rpOpts = await this.getRPOptions(context, { queryId, responseRedirectURI: responseRedirectURI })
283
316
  if (!rpOpts.identifierOpts.resolveOpts?.resolver || typeof rpOpts.identifierOpts.resolveOpts.resolver.resolve !== 'function') {
@@ -292,9 +325,9 @@ export class SIOPv2RP implements IAgentPlugin {
292
325
  resolverResolution: true,
293
326
  })
294
327
  }
295
- this.instances.set(instanceId, new RPInstance({ rpOpts, pexOpts: instanceOpts }))
328
+ rpInstance = new RPInstance({ rpOpts, pexOpts: instanceOpts })
329
+ this.instances.set(rpInstanceId, rpInstance)
296
330
  }
297
- const rpInstance = this.instances.get(instanceId)!
298
331
  if (responseRedirectURI) {
299
332
  rpInstance.rpOptions.responseRedirectUri = responseRedirectURI
300
333
  }
@@ -336,22 +369,22 @@ export class SIOPv2RP implements IAgentPlugin {
336
369
  return options
337
370
  }
338
371
 
339
- getInstanceOpts(definitionId?: string): IPEXInstanceOptions | undefined {
372
+ getInstanceOpts(queryId?: string): IPEXInstanceOptions | undefined {
340
373
  if (!this.opts.instanceOpts) return undefined
341
374
 
342
- const instanceOpt = definitionId ? this.opts.instanceOpts.find((i) => i.queryId === definitionId) : undefined
375
+ const instanceOpt = queryId ? this.opts.instanceOpts.find((i) => i.queryId === queryId) : undefined
343
376
 
344
- return instanceOpt ?? this.getDefaultOptions(definitionId)
377
+ return instanceOpt ?? this.getDefaultOptions(queryId)
345
378
  }
346
379
 
347
- private getDefaultOptions(definitionId: string | undefined) {
380
+ private getDefaultOptions(queryId: string | undefined) {
348
381
  if (!this.opts.instanceOpts) return undefined
349
382
 
350
383
  const defaultOptions = this.opts.instanceOpts.find((i) => i.queryId === 'default')
351
384
  if (defaultOptions) {
352
385
  const clonedOptions = { ...defaultOptions }
353
- if (definitionId !== undefined) {
354
- clonedOptions.queryId = definitionId
386
+ if (queryId !== undefined) {
387
+ clonedOptions.queryId = queryId
355
388
  }
356
389
  return clonedOptions
357
390
  }
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,
@@ -14,7 +15,7 @@ import {
14
15
  Scope,
15
16
  SubjectType,
16
17
  SupportedVersion,
17
- VerifyJwtCallback
18
+ VerifyJwtCallback,
18
19
  } from '@sphereon/did-auth-siop'
19
20
  import { CreateJwtCallback, JwtHeader, JwtIssuer, JwtPayload, SigningAlgo } from '@sphereon/oid4vc-common'
20
21
  import { IPresentationDefinition } from '@sphereon/pex'
@@ -34,7 +35,7 @@ import { TKeyType } from '@veramo/core'
34
35
  import { JWTVerifyOptions } from 'did-jwt'
35
36
  import { Resolvable } from 'did-resolver'
36
37
  import { EventEmitter } from 'events'
37
- import { IPEXOptions, IRequiredContext, IRPOptions, ISIOPIdentifierOptions } from './types/ISIOPv2RP'
38
+ import { IRequiredContext, IRPOptions, ISIOPIdentifierOptions } from './types/ISIOPv2RP'
38
39
  import { DcqlQuery } from 'dcql'
39
40
  import { defaultHasher } from '@sphereon/ssi-sdk.core'
40
41
 
@@ -42,7 +43,7 @@ export function getRequestVersion(rpOptions: IRPOptions): SupportedVersion {
42
43
  if (Array.isArray(rpOptions.supportedVersions) && rpOptions.supportedVersions.length > 0) {
43
44
  return rpOptions.supportedVersions[0]
44
45
  }
45
- return SupportedVersion.JWT_VC_PRESENTATION_PROFILE_v1
46
+ return SupportedVersion.OID4VP_v1
46
47
  }
47
48
 
48
49
  function getWellKnownDIDVerifyCallback(siopIdentifierOpts: ISIOPIdentifierOptions, context: IRequiredContext) {
@@ -57,6 +58,31 @@ function getWellKnownDIDVerifyCallback(siopIdentifierOpts: ISIOPIdentifierOption
57
58
  }
58
59
  }
59
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
+
60
86
  export function getPresentationVerificationCallback(
61
87
  idOpts: ManagedIdentifierOptsOrResult,
62
88
  context: IRequiredContext,
@@ -101,34 +127,11 @@ export function getPresentationVerificationCallback(
101
127
 
102
128
  export async function createRPBuilder(args: {
103
129
  rpOpts: IRPOptions
104
- pexOpts?: IPEXOptions | undefined
105
130
  definition?: IPresentationDefinition
106
- dcql?: DcqlQuery
107
131
  context: IRequiredContext
108
132
  }): Promise<RPBuilder> {
109
- const { rpOpts, pexOpts, context } = args
133
+ const { rpOpts, context } = args
110
134
  const { identifierOpts } = rpOpts
111
- let definition: IPresentationDefinition | undefined = args.definition
112
- let dcqlQuery: DcqlQuery | undefined = args.dcql
113
-
114
- if (!definition && pexOpts && pexOpts.queryId) {
115
- const presentationDefinitionItems = await context.agent.pdmGetDefinitions({
116
- filter: [
117
- {
118
- queryId: pexOpts.queryId,
119
- version: pexOpts.version,
120
- tenantId: pexOpts.tenantId,
121
- },
122
- ],
123
- })
124
-
125
- if (presentationDefinitionItems.length > 0) {
126
- const presentationDefinitionItem = presentationDefinitionItems[0]
127
- if (!dcqlQuery && presentationDefinitionItem.dcqlPayload) {
128
- dcqlQuery = presentationDefinitionItem.dcqlPayload.dcqlQuery as DcqlQuery // cast from DcqlQueryREST back to valibot DcqlQuery
129
- }
130
- }
131
- }
132
135
 
133
136
  const didMethods = identifierOpts.supportedDIDMethods ?? (await getAgentDIDMethods(context))
134
137
  const eventEmitter = rpOpts.eventEmitter ?? new EventEmitter()
@@ -168,9 +171,7 @@ export async function createRPBuilder(args: {
168
171
  .withResponseMode(rpOpts.responseMode ?? ResponseMode.POST)
169
172
  .withResponseType(ResponseType.VP_TOKEN, PropertyTarget.REQUEST_OBJECT)
170
173
  // todo: move to options fill/correct method
171
- .withSupportedVersions(
172
- rpOpts.supportedVersions ?? [SupportedVersion.JWT_VC_PRESENTATION_PROFILE_v1, SupportedVersion.SIOPv2_ID1, SupportedVersion.SIOPv2_D11],
173
- )
174
+ .withSupportedVersions(rpOpts.supportedVersions ?? [SupportedVersion.OID4VP_v1, SupportedVersion.SIOPv2_OID4VP_D28])
174
175
 
175
176
  .withEventEmitter(eventEmitter)
176
177
  .withSessionManager(rpOpts.sessionManager ?? new InMemoryRPSessionManager(eventEmitter))
@@ -189,6 +190,7 @@ export async function createRPBuilder(args: {
189
190
  context,
190
191
  ),
191
192
  )
193
+ .withDcqlQueryLookup(getDcqlQueryLookupCallback(context))
192
194
  .withRevocationVerification(RevocationVerification.NEVER)
193
195
  .withPresentationVerification(getPresentationVerificationCallback(identifierOpts.idOpts, context))
194
196
 
@@ -197,11 +199,12 @@ export async function createRPBuilder(args: {
197
199
  builder.withEntityId(oidfOpts.identifier, PropertyTarget.REQUEST_OBJECT)
198
200
  } else {
199
201
  const resolution = await context.agent.identifierManagedGet(identifierOpts.idOpts)
200
- const clientId: string = rpOpts.clientMetadataOpts?.client_id ??
201
- resolution.issuer ?? (isManagedIdentifierDidResult(resolution) ? resolution.did : resolution.jwkThumbprint)
202
- const clientIdPrefixed = prefixClientId(clientId)
203
- builder.withClientId(clientIdPrefixed, PropertyTarget.REQUEST_OBJECT
204
- )
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)
205
208
  }
206
209
 
207
210
  if (hasher) {
@@ -215,10 +218,6 @@ export async function createRPBuilder(args: {
215
218
  //fixme: this has been removed in the new version of did-auth-siop
216
219
  // builder.withWellknownDIDVerifyCallback(getWellKnownDIDVerifyCallback(didOpts, context))
217
220
 
218
- if (dcqlQuery) {
219
- builder.withDcqlQuery(dcqlQuery)
220
- }
221
-
222
221
  if (rpOpts.responseRedirectUri) {
223
222
  builder.withResponseRedirectUri(rpOpts.responseRedirectUri)
224
223
  }
@@ -303,8 +302,8 @@ export function getSigningAlgo(type: TKeyType): SigningAlgo {
303
302
  export function prefixClientId(clientId: string): string {
304
303
  // FIXME SSISDK-60
305
304
  if (clientId.startsWith('did:')) {
306
- return `${ClientIdentifierPrefix.DECENTRALIZED_IDENTIFIER}:${clientId}`;
305
+ return `${ClientIdentifierPrefix.DECENTRALIZED_IDENTIFIER}:${clientId}`
307
306
  }
308
307
 
309
- return clientId;
308
+ return clientId
310
309
  }
@@ -136,11 +136,12 @@ export interface IPEXDefinitionPersistArgs extends IPEXInstanceOptions {
136
136
  }
137
137
 
138
138
  export interface ISiopRPInstanceArgs {
139
+ createWhenNotPresent: boolean
139
140
  queryId?: string
140
141
  responseRedirectURI?: string
141
142
  }
142
143
 
143
- export interface IPEXInstanceOptions extends IPEXOptions {
144
+ export interface IPEXInstanceOptions extends IPresentationOptions {
144
145
  rpOpts?: IRPOptions
145
146
  }
146
147
 
@@ -158,12 +159,9 @@ export interface IRPOptions {
158
159
  responseRedirectUri?: string
159
160
  }
160
161
 
161
- export interface IPEXOptions {
162
- presentationVerifyCallback?: PresentationVerificationCallback
163
- // definition?: IPresentationDefinition
162
+ export interface IPresentationOptions {
164
163
  queryId: string
165
- version?: string
166
- tenantId?: string
164
+ presentationVerifyCallback?: PresentationVerificationCallback
167
165
  }
168
166
 
169
167
  export type VerificationPolicies = {