@sphereon/ssi-sdk.siopv2-oid4vp-op-auth 0.32.1-next.54 → 0.33.0

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.
Files changed (38) hide show
  1. package/dist/agent/DidAuthSiopOpAuthenticator.d.ts +6 -1
  2. package/dist/agent/DidAuthSiopOpAuthenticator.d.ts.map +1 -1
  3. package/dist/agent/DidAuthSiopOpAuthenticator.js +91 -22
  4. package/dist/agent/DidAuthSiopOpAuthenticator.js.map +1 -1
  5. package/dist/services/Siopv2MachineService.d.ts +4 -2
  6. package/dist/services/Siopv2MachineService.d.ts.map +1 -1
  7. package/dist/services/Siopv2MachineService.js +111 -13
  8. package/dist/services/Siopv2MachineService.js.map +1 -1
  9. package/dist/session/OID4VP.d.ts +4 -4
  10. package/dist/session/OID4VP.d.ts.map +1 -1
  11. package/dist/session/OID4VP.js +2 -1
  12. package/dist/session/OID4VP.js.map +1 -1
  13. package/dist/session/OpSession.d.ts.map +1 -1
  14. package/dist/session/OpSession.js +2 -2
  15. package/dist/session/OpSession.js.map +1 -1
  16. package/dist/types/IDidAuthSiopOpAuthenticator.d.ts +11 -7
  17. package/dist/types/IDidAuthSiopOpAuthenticator.d.ts.map +1 -1
  18. package/dist/types/IDidAuthSiopOpAuthenticator.js.map +1 -1
  19. package/dist/types/siop-service/index.d.ts +4 -2
  20. package/dist/types/siop-service/index.d.ts.map +1 -1
  21. package/dist/types/siop-service/index.js.map +1 -1
  22. package/dist/utils/CredentialUtils.d.ts +23 -0
  23. package/dist/utils/CredentialUtils.d.ts.map +1 -0
  24. package/dist/utils/CredentialUtils.js +65 -0
  25. package/dist/utils/CredentialUtils.js.map +1 -0
  26. package/dist/utils/dcql.d.ts +5 -0
  27. package/dist/utils/dcql.d.ts.map +1 -0
  28. package/dist/utils/dcql.js +37 -0
  29. package/dist/utils/dcql.js.map +1 -0
  30. package/package.json +26 -23
  31. package/src/agent/DidAuthSiopOpAuthenticator.ts +122 -42
  32. package/src/services/Siopv2MachineService.ts +130 -20
  33. package/src/session/OID4VP.ts +8 -8
  34. package/src/session/OpSession.ts +5 -4
  35. package/src/types/IDidAuthSiopOpAuthenticator.ts +20 -7
  36. package/src/types/siop-service/index.ts +9 -6
  37. package/src/utils/CredentialUtils.ts +71 -0
  38. package/src/utils/dcql.ts +36 -0
@@ -2,19 +2,22 @@ import { decodeUriAsJson, PresentationSignCallback, SupportedVersion, VerifiedAu
2
2
  import {
3
3
  ConnectionType,
4
4
  CorrelationIdentifierType,
5
+ CredentialDocumentFormat,
5
6
  CredentialRole,
7
+ DocumentType,
6
8
  Identity,
7
9
  IdentityOrigin,
8
10
  NonPersistedIdentity,
9
11
  Party,
10
12
  } from '@sphereon/ssi-sdk.data-store'
11
- import { Hasher, Loggers } from '@sphereon/ssi-types'
13
+ import { HasherSync, Loggers, SdJwtDecodedVerifiableCredential } from '@sphereon/ssi-types'
12
14
  import { IAgentPlugin } from '@veramo/core'
13
15
  import { v4 as uuidv4 } from 'uuid'
14
16
  import {
15
17
  DidAuthSiopOpAuthenticatorOptions,
16
18
  GetSelectableCredentialsArgs,
17
19
  IOpSessionArgs,
20
+ Json,
18
21
  LOGGER_NAMESPACE,
19
22
  RequiredContext,
20
23
  schema,
@@ -30,16 +33,19 @@ import { computeEntryHash } from '@veramo/utils'
30
33
  import { UniqueDigitalCredential } from '@sphereon/ssi-sdk.credential-store'
31
34
  import { EventEmitter } from 'events'
32
35
  import {
33
- AddIdentityArgs,
34
- CreateConfigArgs,
35
- CreateConfigResult,
36
- GetSiopRequestArgs,
37
36
  IDidAuthSiopOpAuthenticator,
38
37
  IGetSiopSessionArgs,
39
38
  IRegisterCustomApprovalForSiopArgs,
40
39
  IRemoveCustomApprovalForSiopArgs,
41
40
  IRemoveSiopSessionArgs,
42
41
  IRequiredContext,
42
+ } from '../types'
43
+
44
+ import {
45
+ AddIdentityArgs,
46
+ CreateConfigArgs,
47
+ CreateConfigResult,
48
+ GetSiopRequestArgs,
43
49
  OnContactIdentityCreatedArgs,
44
50
  OnIdentifierCreatedArgs,
45
51
  RetrieveContactArgs,
@@ -47,8 +53,9 @@ import {
47
53
  Siopv2AuthorizationRequestData,
48
54
  Siopv2HolderEvent,
49
55
  Siopv2Machine as Siopv2MachineId,
50
- Siopv2MachineInstanceOpts
56
+ Siopv2MachineInstanceOpts,
51
57
  } from '../types'
58
+ import { DcqlCredential, DcqlPresentation, DcqlQuery, DcqlSdJwtVcCredential } from 'dcql'
52
59
 
53
60
  const logger = Loggers.DEFAULT.options(LOGGER_NAMESPACE, {}).get(LOGGER_NAMESPACE)
54
61
 
@@ -84,22 +91,16 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
84
91
  siopGetSelectableCredentials: this.siopGetSelectableCredentials.bind(this),
85
92
  }
86
93
 
87
- private readonly hasher?: Hasher
88
94
  private readonly sessions: Map<string, OpSession>
89
95
  private readonly customApprovals: Record<string, (verifiedAuthorizationRequest: VerifiedAuthorizationRequest, sessionId: string) => Promise<void>>
90
96
  private readonly presentationSignCallback?: PresentationSignCallback
91
97
  private readonly onContactIdentityCreated?: (args: OnContactIdentityCreatedArgs) => Promise<void>
92
98
  private readonly onIdentifierCreated?: (args: OnIdentifierCreatedArgs) => Promise<void>
93
99
  private readonly eventEmitter?: EventEmitter
100
+ private readonly hasher?: HasherSync
94
101
 
95
102
  constructor(options?: DidAuthSiopOpAuthenticatorOptions) {
96
- const {
97
- onContactIdentityCreated,
98
- onIdentifierCreated,
99
- hasher,
100
- customApprovals = {},
101
- presentationSignCallback
102
- } = { ...options }
103
+ const { onContactIdentityCreated, onIdentifierCreated, hasher, customApprovals = {}, presentationSignCallback } = { ...options }
103
104
 
104
105
  this.hasher = hasher
105
106
  this.onContactIdentityCreated = onContactIdentityCreated
@@ -214,9 +215,14 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
214
215
  }
215
216
  const { sessionId, redirectUrl } = didAuthConfig
216
217
 
217
- const session: OpSession = await agent
218
- .siopGetOPSession({ sessionId })
219
- .catch(async () => await agent.siopRegisterOPSession({ requestJwtOrUri: redirectUrl, sessionId, op: { eventEmitter: this.eventEmitter, hasher: this.hasher } }))
218
+ const session: OpSession = await agent.siopGetOPSession({ sessionId }).catch(
219
+ async () =>
220
+ await agent.siopRegisterOPSession({
221
+ requestJwtOrUri: redirectUrl,
222
+ sessionId,
223
+ op: { eventEmitter: this.eventEmitter, hasher: this.hasher },
224
+ }),
225
+ )
220
226
 
221
227
  logger.debug(`session: ${JSON.stringify(session.id, null, 2)}`)
222
228
  const verifiedAuthorizationRequest = await session.getAuthorizationRequest()
@@ -245,6 +251,7 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
245
251
  verifiedAuthorizationRequest.presentationDefinitions.length > 0)
246
252
  ? verifiedAuthorizationRequest.presentationDefinitions
247
253
  : undefined,
254
+ dcqlQuery: verifiedAuthorizationRequest.dcqlQuery,
248
255
  }
249
256
  }
250
257
 
@@ -345,32 +352,75 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
345
352
 
346
353
  const pex = new PEX({ hasher: this.hasher })
347
354
  const verifiableCredentialsWithDefinition: Array<VerifiableCredentialsWithDefinition> = []
348
-
349
- authorizationRequestData.presentationDefinitions?.forEach((presentationDefinition) => {
350
- const { areRequiredCredentialsPresent, verifiableCredential: verifiableCredentials } = pex.selectFrom(
351
- presentationDefinition.definition,
352
- selectedCredentials.map((udc) => udc.originalVerifiableCredential!),
353
- )
354
- if (areRequiredCredentialsPresent !== Status.ERROR && verifiableCredentials) {
355
- const uniqueDigitalCredentials: UniqueDigitalCredential[] = verifiableCredentials.map((vc) => {
356
- // @ts-ignore FIXME Funke
357
- const hash = computeEntryHash(vc)
358
- const udc = selectedCredentials.find((udc) => udc.hash == hash)
359
-
360
- if (!udc) {
361
- throw Error('UniqueDigitalCredential could not be found')
355
+ const dcqlCredentialsWithCredentials: Map<DcqlCredential, UniqueDigitalCredential> = new Map()
356
+
357
+ if (Array.isArray(authorizationRequestData.presentationDefinitions) && authorizationRequestData?.presentationDefinitions.length > 0) {
358
+ try {
359
+ authorizationRequestData.presentationDefinitions?.forEach((presentationDefinition) => {
360
+ const { areRequiredCredentialsPresent, verifiableCredential: verifiableCredentials } = pex.selectFrom(
361
+ presentationDefinition.definition,
362
+ selectedCredentials.map((udc) => udc.originalVerifiableCredential!),
363
+ )
364
+
365
+ if (areRequiredCredentialsPresent !== Status.ERROR && verifiableCredentials) {
366
+ let uniqueDigitalCredentials: UniqueDigitalCredential[] = []
367
+ uniqueDigitalCredentials = verifiableCredentials.map((vc) => {
368
+ // @ts-ignore FIXME Funke
369
+ const hash = typeof vc === 'string' ? computeEntryHash(vc.split('~'[0])) : computeEntryHash(vc)
370
+ const udc = selectedCredentials.find((udc) => udc.hash == hash || udc.originalVerifiableCredential == vc)
371
+
372
+ if (!udc) {
373
+ throw Error(
374
+ `UniqueDigitalCredential could not be found in store. Either the credential is not present in the store or the hash is not correct.`,
375
+ )
376
+ }
377
+ return udc
378
+ })
379
+ verifiableCredentialsWithDefinition.push({
380
+ definition: presentationDefinition,
381
+ credentials: uniqueDigitalCredentials,
382
+ })
362
383
  }
363
- return udc
364
- })
365
- verifiableCredentialsWithDefinition.push({
366
- definition: presentationDefinition,
367
- credentials: uniqueDigitalCredentials,
368
384
  })
385
+ } catch (e) {
386
+ return Promise.reject(e)
369
387
  }
370
- })
371
388
 
372
- if (verifiableCredentialsWithDefinition.length === 0) {
373
- return Promise.reject(Error('None of the selected credentials match any of the presentation definitions.'))
389
+ if (verifiableCredentialsWithDefinition.length === 0) {
390
+ return Promise.reject(Error('None of the selected credentials match any of the presentation definitions.'))
391
+ }
392
+ } else if (authorizationRequestData.dcqlQuery) {
393
+ //TODO Only SD-JWT and MSO MDOC are supported at the moment
394
+ if (this.hasMDocCredentials(selectedCredentials) || this.hasSdJwtCredentials(selectedCredentials)) {
395
+ try {
396
+ selectedCredentials.forEach((vc) => {
397
+ if (this.isSdJwtCredential(vc)) {
398
+ const payload = (vc.originalVerifiableCredential as SdJwtDecodedVerifiableCredential).decodedPayload
399
+ const result: DcqlSdJwtVcCredential = {
400
+ claims: payload as { [x: string]: Json },
401
+ vct: payload.vct,
402
+ credential_format: 'vc+sd-jwt',
403
+ }
404
+ dcqlCredentialsWithCredentials.set(result, vc)
405
+ //FIXME MDoc namespaces are incompatible: array of strings vs complex object - https://sphereon.atlassian.net/browse/SPRIND-143
406
+ } else {
407
+ throw Error(`Invalid credential format: ${vc.digitalCredential.documentFormat}`)
408
+ }
409
+ })
410
+ } catch (e) {
411
+ return Promise.reject(e)
412
+ }
413
+
414
+ const dcqlPresentationRecord: DcqlPresentation.Output = {}
415
+ const queryResult = DcqlQuery.query(authorizationRequestData.dcqlQuery, Array.from(dcqlCredentialsWithCredentials.keys()))
416
+ for (const [key, value] of Object.entries(queryResult.credential_matches)) {
417
+ if (value.success) {
418
+ dcqlPresentationRecord[key] = this.retrieveEncodedCredential(dcqlCredentialsWithCredentials.get(value.output)!) as
419
+ | string
420
+ | { [x: string]: Json }
421
+ }
422
+ }
423
+ }
374
424
  }
375
425
 
376
426
  const response = await siopSendAuthorizationResponse(
@@ -380,7 +430,7 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
380
430
  ...(args.idOpts && { idOpts: args.idOpts }),
381
431
  ...(authorizationRequestData.presentationDefinitions !== undefined && { verifiableCredentialsWithDefinition }),
382
432
  isFirstParty,
383
- hasher: this.hasher
433
+ hasher: this.hasher,
384
434
  },
385
435
  context,
386
436
  )
@@ -395,11 +445,41 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
395
445
 
396
446
  return {
397
447
  body: responseBody,
398
- url: response.url,
399
- queryParams: decodeUriAsJson(response.url),
448
+ url: response?.url,
449
+ queryParams: decodeUriAsJson(response?.url),
400
450
  }
401
451
  }
402
452
 
453
+ private hasMDocCredentials = (credentials: UniqueDigitalCredential[]): boolean => {
454
+ return credentials.some(this.isMDocCredential)
455
+ }
456
+
457
+ private isMDocCredential = (credential: UniqueDigitalCredential) => {
458
+ return (
459
+ credential.digitalCredential.documentFormat === CredentialDocumentFormat.MSO_MDOC &&
460
+ credential.digitalCredential.documentType === DocumentType.VC
461
+ )
462
+ }
463
+
464
+ private hasSdJwtCredentials = (credentials: UniqueDigitalCredential[]): boolean => {
465
+ return credentials.some(this.isSdJwtCredential)
466
+ }
467
+
468
+ private isSdJwtCredential = (credential: UniqueDigitalCredential) => {
469
+ return (
470
+ credential.digitalCredential.documentFormat === CredentialDocumentFormat.SD_JWT && credential.digitalCredential.documentType === DocumentType.VC
471
+ )
472
+ }
473
+
474
+ private retrieveEncodedCredential = (credential: UniqueDigitalCredential) => {
475
+ return credential.originalVerifiableCredential !== undefined &&
476
+ credential.originalVerifiableCredential !== null &&
477
+ (credential?.originalVerifiableCredential as SdJwtDecodedVerifiableCredential)?.compactSdJwtVc !== undefined &&
478
+ (credential?.originalVerifiableCredential as SdJwtDecodedVerifiableCredential)?.compactSdJwtVc !== null
479
+ ? (credential.originalVerifiableCredential as SdJwtDecodedVerifiableCredential).compactSdJwtVc
480
+ : credential.originalVerifiableCredential
481
+ }
482
+
403
483
  private async siopGetSelectableCredentials(args: GetSelectableCredentialsArgs, context: RequiredContext): Promise<SelectableCredentialsMap> {
404
484
  const { authorizationRequestData } = args
405
485
 
@@ -2,9 +2,9 @@ import { AuthorizationRequest, SupportedVersion } from '@sphereon/did-auth-siop'
2
2
  import { IPresentationDefinition, PEX } from '@sphereon/pex'
3
3
  import { InputDescriptorV1, InputDescriptorV2, PresentationDefinitionV1, PresentationDefinitionV2 } from '@sphereon/pex-models'
4
4
  import { isOID4VCIssuerIdentifier, ManagedIdentifierOptsOrResult } from '@sphereon/ssi-sdk-ext.identifier-resolution'
5
- import { verifiableCredentialForRoleFilter } from '@sphereon/ssi-sdk.credential-store'
5
+ import { UniqueDigitalCredential, verifiableCredentialForRoleFilter } from '@sphereon/ssi-sdk.credential-store'
6
6
  import { ConnectionType, CredentialRole } from '@sphereon/ssi-sdk.data-store'
7
- import { CredentialMapper, Hasher, Loggers, PresentationSubmission } from '@sphereon/ssi-types'
7
+ import { CredentialMapper, HasherSync, Loggers, OriginalVerifiableCredential, PresentationSubmission } from '@sphereon/ssi-types'
8
8
  import { OID4VP, OpSession } from '../session'
9
9
  import {
10
10
  DidAgents,
@@ -19,7 +19,10 @@ import {
19
19
  } from '../types'
20
20
  import { IAgentContext, IDIDManager } from '@veramo/core'
21
21
  import { getOrCreatePrimaryIdentifier, SupportedDidMethodEnum } from '@sphereon/ssi-sdk-ext.did-utils'
22
- import { encodeJoseBlob } from '@sphereon/ssi-sdk.core'
22
+ import { defaultHasher, encodeJoseBlob } from '@sphereon/ssi-sdk.core'
23
+ import { DcqlCredential, DcqlCredentialPresentation, DcqlPresentation, DcqlQuery } from 'dcql'
24
+ import { convertToDcqlCredentials } from '../utils/dcql'
25
+ import { getOriginalVerifiableCredential } from '../utils/CredentialUtils'
23
26
 
24
27
  export const logger = Loggers.DEFAULT.get(LOGGER_NAMESPACE)
25
28
 
@@ -49,13 +52,14 @@ export const siopSendAuthorizationResponse = async (
49
52
  verifiableCredentialsWithDefinition?: VerifiableCredentialsWithDefinition[]
50
53
  idOpts?: ManagedIdentifierOptsOrResult
51
54
  isFirstParty?: boolean
52
- hasher?: Hasher
55
+ hasher?: HasherSync
56
+ dcqlQuery?: DcqlQuery
53
57
  },
54
58
  context: RequiredContext,
55
59
  ) => {
56
60
  const { agent } = context
57
61
  const agentContext = { ...context, agent: context.agent as DidAgents }
58
- let { idOpts, isFirstParty, hasher } = args
62
+ let { idOpts, isFirstParty, hasher = defaultHasher } = args
59
63
 
60
64
  if (connectionType !== ConnectionType.SIOPv2_OpenID4VP) {
61
65
  return Promise.reject(Error(`No supported authentication provider for type: ${connectionType}`))
@@ -125,11 +129,18 @@ export const siopSendAuthorizationResponse = async (
125
129
  break
126
130
  // TODO other implementations?
127
131
  default:
128
- // Since we are using the kmsKeyRef we will find the KID regardless of the identifier. We set it for later access though
129
- identifier = await session.context.agent.identifierManagedGetByKid({
130
- identifier: digitalCredential.subjectCorrelationId ?? holder ?? digitalCredential.kmsKeyRef,
131
- kmsKeyRef: digitalCredential.kmsKeyRef,
132
- })
132
+ if (digitalCredential.subjectCorrelationId?.startsWith('did:') || holder?.startsWith('did:')) {
133
+ identifier = await session.context.agent.identifierManagedGetByDid({
134
+ identifier: digitalCredential.subjectCorrelationId ?? holder,
135
+ kmsKeyRef: digitalCredential.kmsKeyRef,
136
+ })
137
+ } else {
138
+ // Since we are using the kmsKeyRef we will find the KID regardless of the identifier. We set it for later access though
139
+ identifier = await session.context.agent.identifierManagedGetByKid({
140
+ identifier: digitalCredential.subjectCorrelationId ?? holder ?? digitalCredential.kmsKeyRef,
141
+ kmsKeyRef: digitalCredential.kmsKeyRef,
142
+ })
143
+ }
133
144
  }
134
145
  }
135
146
 
@@ -155,17 +166,116 @@ export const siopSendAuthorizationResponse = async (
155
166
 
156
167
  idOpts = presentationsAndDefs[0].idOpts
157
168
  presentationSubmission = presentationsAndDefs[0].presentationSubmission
169
+
170
+ logger.log(`Definitions and locations:`, JSON.stringify(presentationsAndDefs?.[0]?.verifiablePresentations, null, 2))
171
+ logger.log(`Presentation Submission:`, JSON.stringify(presentationSubmission, null, 2))
172
+ const mergedVerifiablePresentations = presentationsAndDefs?.flatMap((pd) => pd.verifiablePresentations) || []
173
+ return await session.sendAuthorizationResponse({
174
+ ...(presentationsAndDefs && { verifiablePresentations: mergedVerifiablePresentations }),
175
+ ...(presentationSubmission && { presentationSubmission }),
176
+ // todo: Change issuer value in case we do not use identifier. Use key.meta.jwkThumbprint then
177
+ responseSignerOpts: idOpts!,
178
+ isFirstParty,
179
+ })
180
+ } else if (request.dcqlQuery) {
181
+ if (args.verifiableCredentialsWithDefinition !== undefined && args.verifiableCredentialsWithDefinition !== null) {
182
+ const vcs = args.verifiableCredentialsWithDefinition.flatMap((vcd) => vcd.credentials)
183
+ const domain =
184
+ ((await request.authorizationRequest.getMergedProperty('client_id')) as string) ??
185
+ request.issuer ??
186
+ (request.versions.includes(SupportedVersion.JWT_VC_PRESENTATION_PROFILE_v1)
187
+ ? 'https://self-issued.me/v2/openid-vc'
188
+ : 'https://self-issued.me/v2')
189
+ logger.debug(`NONCE: ${session.nonce}, domain: ${domain}`)
190
+
191
+ const firstUniqueDC = vcs[0]
192
+ if (typeof firstUniqueDC !== 'object' || !('digitalCredential' in firstUniqueDC)) {
193
+ return Promise.reject(Error('SiopMachine only supports UniqueDigitalCredentials for now'))
194
+ }
195
+
196
+ let identifier: ManagedIdentifierOptsOrResult
197
+ const digitalCredential = firstUniqueDC.digitalCredential
198
+ const firstVC = firstUniqueDC.uniformVerifiableCredential
199
+ const holder = CredentialMapper.isSdJwtDecodedCredential(firstVC)
200
+ ? firstVC.decodedPayload.cnf?.jwk
201
+ ? //TODO SDK-19: convert the JWK to hex and search for the appropriate key and associated DID
202
+ //doesn't apply to did:jwk only, as you can represent any DID key as a JWK. So whenever you encounter a JWK it doesn't mean it had to come from a did:jwk in the system. It just can always be represented as a did:jwk
203
+ `did:jwk:${encodeJoseBlob(firstVC.decodedPayload.cnf?.jwk)}#0`
204
+ : firstVC.decodedPayload.sub
205
+ : Array.isArray(firstVC.credentialSubject)
206
+ ? firstVC.credentialSubject[0].id
207
+ : firstVC.credentialSubject.id
208
+ if (!digitalCredential.kmsKeyRef) {
209
+ // In case the store does not have the kmsKeyRef lets search for the holder
210
+
211
+ if (!holder) {
212
+ return Promise.reject(`No holder found and no kmsKeyRef in DB. Cannot determine identifier to use`)
213
+ }
214
+ try {
215
+ identifier = await session.context.agent.identifierManagedGet({ identifier: holder })
216
+ } catch (e) {
217
+ logger.debug(`Holder DID not found: ${holder}`)
218
+ throw e
219
+ }
220
+ } else if (isOID4VCIssuerIdentifier(digitalCredential.kmsKeyRef)) {
221
+ identifier = await session.context.agent.identifierManagedGetByOID4VCIssuer({
222
+ identifier: firstUniqueDC.digitalCredential.kmsKeyRef,
223
+ })
224
+ } else {
225
+ switch (digitalCredential.subjectCorrelationType) {
226
+ case 'DID':
227
+ identifier = await session.context.agent.identifierManagedGetByDid({
228
+ identifier: digitalCredential.subjectCorrelationId ?? holder,
229
+ kmsKeyRef: digitalCredential.kmsKeyRef,
230
+ })
231
+ break
232
+ // TODO other implementations?
233
+ default:
234
+ // Since we are using the kmsKeyRef we will find the KID regardless of the identifier. We set it for later access though
235
+ identifier = await session.context.agent.identifierManagedGetByKid({
236
+ identifier: digitalCredential.subjectCorrelationId ?? holder ?? digitalCredential.kmsKeyRef,
237
+ kmsKeyRef: digitalCredential.kmsKeyRef,
238
+ })
239
+ }
240
+ }
241
+ console.log(`Identifier`, identifier)
242
+
243
+ const dcqlRepresentations: DcqlCredential[] = []
244
+ vcs.forEach((vc: UniqueDigitalCredential | OriginalVerifiableCredential) => {
245
+ const rep = convertToDcqlCredentials(vc, args.hasher)
246
+ if (rep) {
247
+ dcqlRepresentations.push(rep)
248
+ }
249
+ })
250
+
251
+ const queryResult = DcqlQuery.query(request.dcqlQuery, dcqlRepresentations)
252
+ const presentation: Record<string, DcqlCredentialPresentation> = {}
253
+
254
+ for (const [key, value] of Object.entries(queryResult.credential_matches)) {
255
+ const allMatches = Array.isArray(value) ? value : [value]
256
+ allMatches.forEach((match) => {
257
+ if (match.success) {
258
+ const originalCredential = getOriginalVerifiableCredential(vcs[match.input_credential_index])
259
+ if (!originalCredential) {
260
+ throw new Error(`Index ${match.input_credential_index} out of range in credentials array`)
261
+ }
262
+ presentation[key] =
263
+ (originalCredential as any)['compactSdJwtVc'] !== undefined ? (originalCredential as any).compactSdJwtVc : originalCredential
264
+ }
265
+ })
266
+ }
267
+
268
+ const response = session.sendAuthorizationResponse({
269
+ responseSignerOpts: identifier,
270
+ ...{ dcqlQuery: { dcqlPresentation: DcqlPresentation.parse(presentation) } },
271
+ })
272
+
273
+ logger.debug(`Response: `, response)
274
+
275
+ return response
276
+ }
158
277
  }
159
- logger.log(`Definitions and locations:`, JSON.stringify(presentationsAndDefs?.[0]?.verifiablePresentations, null, 2))
160
- logger.log(`Presentation Submission:`, JSON.stringify(presentationSubmission, null, 2))
161
- const mergedVerifiablePresentations = presentationsAndDefs?.flatMap((pd) => pd.verifiablePresentations) || []
162
- return await session.sendAuthorizationResponse({
163
- ...(presentationsAndDefs && { verifiablePresentations: mergedVerifiablePresentations }),
164
- ...(presentationSubmission && { presentationSubmission }),
165
- // todo: Change issuer value in case we do not use identifier. Use key.meta.jwkThumbprint then
166
- responseSignerOpts: idOpts!,
167
- isFirstParty,
168
- })
278
+ throw Error('Presentation Definition or DCQL is required')
169
279
  }
170
280
 
171
281
  function buildPartialPD(
@@ -2,15 +2,15 @@ import { PresentationDefinitionWithLocation, PresentationExchange } from '@spher
2
2
  import { SelectResults, Status, SubmissionRequirementMatch } from '@sphereon/pex'
3
3
  import { Format } from '@sphereon/pex-models'
4
4
  import {
5
- isOID4VCIssuerIdentifier,
6
5
  isManagedIdentifierDidResult,
6
+ isOID4VCIssuerIdentifier,
7
7
  ManagedIdentifierOptsOrResult,
8
8
  ManagedIdentifierResult,
9
9
  } from '@sphereon/ssi-sdk-ext.identifier-resolution'
10
- import { ProofOptions } from '@sphereon/ssi-sdk.core'
10
+ import { defaultHasher, ProofOptions } from '@sphereon/ssi-sdk.core'
11
11
  import { UniqueDigitalCredential, verifiableCredentialForRoleFilter } from '@sphereon/ssi-sdk.credential-store'
12
12
  import { CredentialRole, FindDigitalCredentialArgs } from '@sphereon/ssi-sdk.data-store'
13
- import { CompactJWT, Hasher, IProof, OriginalVerifiableCredential } from '@sphereon/ssi-types'
13
+ import { CompactJWT, HasherSync, IProof, OriginalVerifiableCredential } from '@sphereon/ssi-types'
14
14
  import {
15
15
  DEFAULT_JWT_PROOF_TYPE,
16
16
  IGetPresentationExchangeArgs,
@@ -24,17 +24,17 @@ import { OpSession } from './OpSession'
24
24
  export class OID4VP {
25
25
  private readonly session: OpSession
26
26
  private readonly allIdentifiers: string[]
27
- private readonly hasher?: Hasher
27
+ private readonly hasher?: HasherSync
28
28
 
29
29
  private constructor(args: IOID4VPArgs) {
30
- const { session, allIdentifiers, hasher } = args
30
+ const { session, allIdentifiers, hasher = defaultHasher } = args
31
31
 
32
32
  this.session = session
33
33
  this.allIdentifiers = allIdentifiers ?? []
34
34
  this.hasher = hasher
35
35
  }
36
36
 
37
- public static async init(session: OpSession, allIdentifiers: string[], hasher?: Hasher): Promise<OID4VP> {
37
+ public static async init(session: OpSession, allIdentifiers: string[], hasher?: HasherSync): Promise<OID4VP> {
38
38
  return new OID4VP({ session, allIdentifiers: allIdentifiers ?? (await session.getSupportedDIDs()), hasher })
39
39
  }
40
40
 
@@ -68,7 +68,7 @@ export class OID4VP {
68
68
  skipDidResolution?: boolean
69
69
  holderDID?: string
70
70
  subjectIsHolder?: boolean
71
- hasher?: Hasher
71
+ hasher?: HasherSync
72
72
  applyFilter?: boolean
73
73
  },
74
74
  ): Promise<VerifiablePresentationWithDefinition[]> {
@@ -88,7 +88,7 @@ export class OID4VP {
88
88
  holder?: string
89
89
  subjectIsHolder?: boolean
90
90
  applyFilter?: boolean
91
- hasher?: Hasher
91
+ hasher?: HasherSync
92
92
  },
93
93
  ): Promise<VerifiablePresentationWithDefinition> {
94
94
  const { subjectIsHolder, holder, forceNoCredentialsInVP = false } = { ...opts }
@@ -20,7 +20,7 @@ import { encodeBase64url } from '@sphereon/ssi-sdk.core'
20
20
  import {
21
21
  CompactSdJwtVc,
22
22
  CredentialMapper,
23
- Hasher,
23
+ HasherSync,
24
24
  OriginalVerifiableCredential,
25
25
  parseDid,
26
26
  PresentationSubmission,
@@ -293,8 +293,8 @@ export class OpSession {
293
293
  .jwtEncryptJweCompactJwt({
294
294
  recipientKey,
295
295
  protectedHeader: {},
296
- alg: requestObjectPayload.client_metadata.authorization_encrypted_response_alg as JweAlg | undefined ?? 'ECDH-ES',
297
- enc: requestObjectPayload.client_metadata.authorization_encrypted_response_enc as JweEnc | undefined ?? 'A256GCM',
296
+ alg: (requestObjectPayload.client_metadata.authorization_encrypted_response_alg as JweAlg | undefined) ?? 'ECDH-ES',
297
+ enc: (requestObjectPayload.client_metadata.authorization_encrypted_response_enc as JweEnc | undefined) ?? 'A256GCM',
298
298
  apv: encodeBase64url(opts.requestObjectPayload.nonce),
299
299
  apu: encodeBase64url(v4()),
300
300
  payload: authResponse,
@@ -367,6 +367,7 @@ export class OpSession {
367
367
  presentationSubmission: args.presentationSubmission,
368
368
  } as PresentationExchangeResponseOpts,
369
369
  }),
370
+ dcqlQuery: args.dcqlResponse,
370
371
  }
371
372
 
372
373
  const authResponse = await op.createAuthorizationResponse(request, responseOpts)
@@ -379,7 +380,7 @@ export class OpSession {
379
380
  }
380
381
  }
381
382
 
382
- private countVCsInAllVPs(verifiablePresentations: W3CVerifiablePresentation[], hasher?: Hasher) {
383
+ private countVCsInAllVPs(verifiablePresentations: W3CVerifiablePresentation[], hasher?: HasherSync) {
383
384
  return verifiablePresentations.reduce((sum, vp) => {
384
385
  if (CredentialMapper.isMsoMdocDecodedPresentation(vp) || CredentialMapper.isMsoMdocOid4VPEncoded(vp)) {
385
386
  return sum + 1
@@ -1,4 +1,5 @@
1
1
  import {
2
+ DcqlResponseOpts,
2
3
  PresentationDefinitionWithLocation,
3
4
  PresentationSignCallback,
4
5
  ResponseMode,
@@ -18,7 +19,7 @@ import { ICredentialStore, UniqueDigitalCredential } from '@sphereon/ssi-sdk.cre
18
19
  import { Party } from '@sphereon/ssi-sdk.data-store'
19
20
  import { IPDManager } from '@sphereon/ssi-sdk.pd-manager'
20
21
  import { ISDJwtPlugin } from '@sphereon/ssi-sdk.sd-jwt'
21
- import { Hasher, OriginalVerifiableCredential, PresentationSubmission, W3CVerifiablePresentation } from '@sphereon/ssi-types'
22
+ import { HasherSync, OriginalVerifiableCredential, PresentationSubmission, W3CVerifiablePresentation } from '@sphereon/ssi-types'
22
23
  import { VerifyCallback } from '@sphereon/wellknown-dids-client'
23
24
  import {
24
25
  IAgentContext,
@@ -122,7 +123,8 @@ export interface IOpsSendSiopAuthorizationResponseArgs {
122
123
  // verifiedAuthorizationRequest: VerifiedAuthorizationRequest
123
124
  presentationSubmission?: PresentationSubmission
124
125
  verifiablePresentations?: W3CVerifiablePresentation[]
125
- hasher?: Hasher
126
+ dcqlResponse?: DcqlResponseOpts
127
+ hasher?: HasherSync
126
128
  isFirstParty?: boolean
127
129
  }
128
130
 
@@ -160,7 +162,7 @@ export interface IOPOptions {
160
162
  presentationSignCallback?: PresentationSignCallback
161
163
 
162
164
  resolveOpts?: ResolveOpts
163
- hasher?: Hasher
165
+ hasher?: HasherSync
164
166
  }
165
167
 
166
168
  /*
@@ -183,19 +185,30 @@ export interface VerifiablePresentationWithDefinition extends VerifiablePresenta
183
185
 
184
186
  export interface IOpSessionGetOID4VPArgs {
185
187
  allIdentifiers?: string[]
186
- hasher?: Hasher
188
+ hasher?: HasherSync
187
189
  }
188
190
 
189
191
  export interface IOID4VPArgs {
190
192
  session: OpSession
191
193
  allIdentifiers?: string[]
192
- hasher?: Hasher
194
+ hasher?: HasherSync
193
195
  }
194
196
 
195
197
  export interface IGetPresentationExchangeArgs {
196
198
  verifiableCredentials: OriginalVerifiableCredential[]
197
199
  allIdentifiers?: string[]
198
- hasher?: Hasher
199
- }
200
+ hasher?: HasherSync
201
+ }
202
+
203
+ // It was added here because it's not exported from DCQL anymore
204
+ export type Json =
205
+ | string
206
+ | number
207
+ | boolean
208
+ | null
209
+ | {
210
+ [key: string]: Json
211
+ }
212
+ | Json[]
200
213
 
201
214
  export const DEFAULT_JWT_PROOF_TYPE = 'JwtProof2020'
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  PresentationDefinitionWithLocation,
3
3
  PresentationSignCallback,
4
- RPRegistrationMetadataPayload, VerifiedAuthorizationRequest
4
+ RPRegistrationMetadataPayload,
5
+ VerifiedAuthorizationRequest,
5
6
  } from '@sphereon/did-auth-siop'
6
7
  import { IIdentifierResolution, ManagedIdentifierOptsOrResult } from '@sphereon/ssi-sdk-ext.identifier-resolution'
7
8
  import { IContactManager } from '@sphereon/ssi-sdk.contact-manager'
@@ -11,14 +12,15 @@ import { IIssuanceBranding } from '@sphereon/ssi-sdk.issuance-branding'
11
12
  import { IAgentContext, IDIDManager, IIdentifier, IResolver } from '@veramo/core'
12
13
  import { IDidAuthSiopOpAuthenticator } from '../IDidAuthSiopOpAuthenticator'
13
14
  import { Siopv2MachineContext, Siopv2MachineInterpreter, Siopv2MachineState } from '../machine'
14
- import { Hasher } from '@sphereon/ssi-types'
15
+ import { DcqlQuery } from 'dcql'
16
+ import { HasherSync } from '@sphereon/ssi-types'
15
17
 
16
18
  export type DidAuthSiopOpAuthenticatorOptions = {
17
19
  presentationSignCallback?: PresentationSignCallback
18
20
  customApprovals?: Record<string, (verifiedAuthorizationRequest: VerifiedAuthorizationRequest, sessionId: string) => Promise<void>>
19
21
  onContactIdentityCreated?: (args: OnContactIdentityCreatedArgs) => Promise<void>
20
22
  onIdentifierCreated?: (args: OnIdentifierCreatedArgs) => Promise<void>
21
- hasher?: Hasher
23
+ hasher?: HasherSync
22
24
  }
23
25
 
24
26
  export type GetMachineArgs = {
@@ -29,14 +31,14 @@ export type GetMachineArgs = {
29
31
 
30
32
  export type CreateConfigArgs = { url: string }
31
33
  export type CreateConfigResult = Omit<DidAuthConfig, 'stateId' | 'idOpts'>
32
- export type GetSiopRequestArgs = { didAuthConfig?: Omit<DidAuthConfig, 'identifier'>, url: string }
34
+ export type GetSiopRequestArgs = { didAuthConfig?: Omit<DidAuthConfig, 'identifier'>; url: string }
33
35
  // FIXME it would be nicer if these function are not tied to a certain machine so that we can start calling them for anywhere
34
36
  export type RetrieveContactArgs = Pick<Siopv2MachineContext, 'url' | 'authorizationRequestData'>
35
37
  // FIXME it would be nicer if these function are not tied to a certain machine so that we can start calling them for anywhere
36
38
  export type AddIdentityArgs = Pick<Siopv2MachineContext, 'contact' | 'authorizationRequestData'>
37
39
  export type SendResponseArgs = {
38
- didAuthConfig?: Omit<DidAuthConfig, 'identifier'>,
39
- authorizationRequestData?: Siopv2AuthorizationRequestData,
40
+ didAuthConfig?: Omit<DidAuthConfig, 'identifier'>
41
+ authorizationRequestData?: Siopv2AuthorizationRequestData
40
42
  selectedCredentials: Array<UniqueDigitalCredential>
41
43
  idOpts?: ManagedIdentifierOptsOrResult
42
44
  isFirstParty?: boolean
@@ -68,6 +70,7 @@ export type Siopv2AuthorizationRequestData = {
68
70
  uri?: URL
69
71
  clientId?: string
70
72
  presentationDefinitions?: PresentationDefinitionWithLocation[]
73
+ dcqlQuery?: DcqlQuery
71
74
  }
72
75
 
73
76
  export type SelectableCredentialsMap = Map<string, Array<SelectableCredential>>