@sphereon/ssi-sdk.siopv2-oid4vp-op-auth 0.32.1-next.54 → 0.33.1-feature.jose.vcdm.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist/index.cjs +2451 -0
  2. package/dist/index.cjs.map +1 -0
  3. package/dist/index.d.cts +559 -0
  4. package/dist/index.d.ts +555 -8
  5. package/dist/index.js +2420 -31
  6. package/dist/index.js.map +1 -1
  7. package/package.json +44 -30
  8. package/src/agent/DidAuthSiopOpAuthenticator.ts +132 -53
  9. package/src/services/Siopv2MachineService.ts +130 -20
  10. package/src/session/OID4VP.ts +8 -8
  11. package/src/session/OpSession.ts +18 -17
  12. package/src/types/IDidAuthSiopOpAuthenticator.ts +20 -7
  13. package/src/types/siop-service/index.ts +9 -6
  14. package/src/utils/CredentialUtils.ts +71 -0
  15. package/src/utils/dcql.ts +36 -0
  16. package/dist/agent/DidAuthSiopOpAuthenticator.d.ts +0 -31
  17. package/dist/agent/DidAuthSiopOpAuthenticator.d.ts.map +0 -1
  18. package/dist/agent/DidAuthSiopOpAuthenticator.js +0 -323
  19. package/dist/agent/DidAuthSiopOpAuthenticator.js.map +0 -1
  20. package/dist/index.d.ts.map +0 -1
  21. package/dist/link-handler/index.d.ts +0 -22
  22. package/dist/link-handler/index.d.ts.map +0 -1
  23. package/dist/link-handler/index.js +0 -57
  24. package/dist/link-handler/index.js.map +0 -1
  25. package/dist/localization/Localization.d.ts +0 -9
  26. package/dist/localization/Localization.d.ts.map +0 -1
  27. package/dist/localization/Localization.js +0 -46
  28. package/dist/localization/Localization.js.map +0 -1
  29. package/dist/localization/translations/en.json +0 -9
  30. package/dist/localization/translations/nl.json +0 -8
  31. package/dist/machine/CallbackStateListener.d.ts +0 -3
  32. package/dist/machine/CallbackStateListener.d.ts.map +0 -1
  33. package/dist/machine/CallbackStateListener.js +0 -48
  34. package/dist/machine/CallbackStateListener.js.map +0 -1
  35. package/dist/machine/Siopv2Machine.d.ts +0 -8
  36. package/dist/machine/Siopv2Machine.d.ts.map +0 -1
  37. package/dist/machine/Siopv2Machine.js +0 -364
  38. package/dist/machine/Siopv2Machine.js.map +0 -1
  39. package/dist/services/IdentifierService.d.ts +0 -3
  40. package/dist/services/IdentifierService.d.ts.map +0 -1
  41. package/dist/services/IdentifierService.js +0 -28
  42. package/dist/services/IdentifierService.js.map +0 -1
  43. package/dist/services/Siopv2MachineService.d.ts +0 -16
  44. package/dist/services/Siopv2MachineService.d.ts.map +0 -1
  45. package/dist/services/Siopv2MachineService.js +0 -201
  46. package/dist/services/Siopv2MachineService.js.map +0 -1
  47. package/dist/session/OID4VP.d.ts +0 -72
  48. package/dist/session/OID4VP.d.ts.map +0 -1
  49. package/dist/session/OID4VP.js +0 -223
  50. package/dist/session/OID4VP.js.map +0 -1
  51. package/dist/session/OpSession.d.ts +0 -39
  52. package/dist/session/OpSession.d.ts.map +0 -1
  53. package/dist/session/OpSession.js +0 -365
  54. package/dist/session/OpSession.js.map +0 -1
  55. package/dist/session/functions.d.ts +0 -37
  56. package/dist/session/functions.d.ts.map +0 -1
  57. package/dist/session/functions.js +0 -163
  58. package/dist/session/functions.js.map +0 -1
  59. package/dist/session/index.d.ts +0 -4
  60. package/dist/session/index.d.ts.map +0 -1
  61. package/dist/session/index.js +0 -20
  62. package/dist/session/index.js.map +0 -1
  63. package/dist/types/IDidAuthSiopOpAuthenticator.d.ts +0 -116
  64. package/dist/types/IDidAuthSiopOpAuthenticator.d.ts.map +0 -1
  65. package/dist/types/IDidAuthSiopOpAuthenticator.js +0 -10
  66. package/dist/types/IDidAuthSiopOpAuthenticator.js.map +0 -1
  67. package/dist/types/error/index.d.ts +0 -8
  68. package/dist/types/error/index.d.ts.map +0 -1
  69. package/dist/types/error/index.js +0 -3
  70. package/dist/types/error/index.js.map +0 -1
  71. package/dist/types/identifier/index.d.ts +0 -53
  72. package/dist/types/identifier/index.d.ts.map +0 -1
  73. package/dist/types/identifier/index.js +0 -5
  74. package/dist/types/identifier/index.js.map +0 -1
  75. package/dist/types/index.d.ts +0 -6
  76. package/dist/types/index.d.ts.map +0 -1
  77. package/dist/types/index.js +0 -22
  78. package/dist/types/index.js.map +0 -1
  79. package/dist/types/machine/index.d.ts +0 -124
  80. package/dist/types/machine/index.d.ts.map +0 -1
  81. package/dist/types/machine/index.js +0 -57
  82. package/dist/types/machine/index.js.map +0 -1
  83. package/dist/types/siop-service/index.d.ts +0 -78
  84. package/dist/types/siop-service/index.d.ts.map +0 -1
  85. package/dist/types/siop-service/index.js +0 -14
  86. package/dist/types/siop-service/index.js.map +0 -1
package/package.json CHANGED
@@ -1,43 +1,56 @@
1
1
  {
2
2
  "name": "@sphereon/ssi-sdk.siopv2-oid4vp-op-auth",
3
- "version": "0.32.1-next.54+3b988a2b",
3
+ "version": "0.33.1-feature.jose.vcdm.55+6f02f6f8",
4
4
  "source": "src/index.ts",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ "react-native": "./dist/index.js",
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "require": "./dist/index.cjs"
18
+ }
19
+ },
7
20
  "veramo": {
8
21
  "pluginInterfaces": {
9
22
  "IDidAuthSiopOpAuthenticator": "./src/types/IDidAuthSiopOpAuthenticator.ts"
10
23
  }
11
24
  },
12
25
  "scripts": {
13
- "build": "tsc --build",
14
- "build:clean": "tsc --build --clean && tsc --build"
26
+ "build": "tsup --config ../../tsup.config.ts --tsconfig ../../tsconfig.tsup.json"
15
27
  },
16
28
  "dependencies": {
17
- "@sphereon/did-auth-siop": "0.16.1-next.339",
18
- "@sphereon/did-auth-siop-adapter": "0.16.1-next.339",
19
- "@sphereon/oid4vc-common": "0.16.1-next.339",
29
+ "@sphereon/did-auth-siop": "0.17.1-feature.esm.cjs.39",
30
+ "@sphereon/did-auth-siop-adapter": "0.17.1-feature.esm.cjs.39",
31
+ "@sphereon/oid4vc-common": "0.17.1-feature.esm.cjs.39",
20
32
  "@sphereon/pex": "5.0.0-unstable.28",
21
33
  "@sphereon/pex-models": "^2.3.2",
22
- "@sphereon/ssi-sdk-ext.did-utils": "0.27.0",
23
- "@sphereon/ssi-sdk-ext.identifier-resolution": "0.27.0",
24
- "@sphereon/ssi-sdk-ext.jwt-service": "0.27.0",
25
- "@sphereon/ssi-sdk.contact-manager": "0.32.1-next.54+3b988a2b",
26
- "@sphereon/ssi-sdk.core": "0.32.1-next.54+3b988a2b",
27
- "@sphereon/ssi-sdk.credential-store": "0.32.1-next.54+3b988a2b",
28
- "@sphereon/ssi-sdk.credential-validation": "0.32.1-next.54+3b988a2b",
29
- "@sphereon/ssi-sdk.data-store": "0.32.1-next.54+3b988a2b",
30
- "@sphereon/ssi-sdk.issuance-branding": "0.32.1-next.54+3b988a2b",
31
- "@sphereon/ssi-sdk.pd-manager": "0.32.1-next.54+3b988a2b",
32
- "@sphereon/ssi-sdk.presentation-exchange": "0.32.1-next.54+3b988a2b",
33
- "@sphereon/ssi-sdk.sd-jwt": "0.32.1-next.54+3b988a2b",
34
- "@sphereon/ssi-sdk.siopv2-oid4vp-common": "0.32.1-next.54+3b988a2b",
35
- "@sphereon/ssi-sdk.xstate-machine-persistence": "0.32.1-next.54+3b988a2b",
36
- "@sphereon/ssi-types": "0.32.1-next.54+3b988a2b",
34
+ "@sphereon/ssi-sdk-ext.did-utils": "0.28.1-feature.esm.cjs.18",
35
+ "@sphereon/ssi-sdk-ext.identifier-resolution": "0.28.1-feature.esm.cjs.18",
36
+ "@sphereon/ssi-sdk-ext.jwt-service": "0.28.1-feature.esm.cjs.18",
37
+ "@sphereon/ssi-sdk.contact-manager": "0.33.1-feature.jose.vcdm.55+6f02f6f8",
38
+ "@sphereon/ssi-sdk.core": "0.33.1-feature.jose.vcdm.55+6f02f6f8",
39
+ "@sphereon/ssi-sdk.credential-store": "0.33.1-feature.jose.vcdm.55+6f02f6f8",
40
+ "@sphereon/ssi-sdk.credential-validation": "0.33.1-feature.jose.vcdm.55+6f02f6f8",
41
+ "@sphereon/ssi-sdk.data-store": "0.33.1-feature.jose.vcdm.55+6f02f6f8",
42
+ "@sphereon/ssi-sdk.issuance-branding": "0.33.1-feature.jose.vcdm.55+6f02f6f8",
43
+ "@sphereon/ssi-sdk.pd-manager": "0.33.1-feature.jose.vcdm.55+6f02f6f8",
44
+ "@sphereon/ssi-sdk.presentation-exchange": "0.33.1-feature.jose.vcdm.55+6f02f6f8",
45
+ "@sphereon/ssi-sdk.sd-jwt": "0.33.1-feature.jose.vcdm.55+6f02f6f8",
46
+ "@sphereon/ssi-sdk.siopv2-oid4vp-common": "0.33.1-feature.jose.vcdm.55+6f02f6f8",
47
+ "@sphereon/ssi-sdk.xstate-machine-persistence": "0.33.1-feature.jose.vcdm.55+6f02f6f8",
48
+ "@sphereon/ssi-types": "0.33.1-feature.jose.vcdm.55+6f02f6f8",
37
49
  "@sphereon/wellknown-dids-client": "^0.1.3",
38
50
  "@veramo/core": "4.2.0",
39
51
  "@veramo/credential-w3c": "4.2.0",
40
52
  "cross-fetch": "^3.1.8",
53
+ "dcql": "0.2.19",
41
54
  "did-jwt-vc": "3.1.3",
42
55
  "i18n-js": "^3.9.2",
43
56
  "lodash.memoize": "^4.1.2",
@@ -46,23 +59,25 @@
46
59
  },
47
60
  "devDependencies": {
48
61
  "@sphereon/did-uni-client": "^0.6.3",
49
- "@sphereon/ssi-sdk-ext.did-resolver-jwk": "0.27.0",
50
- "@sphereon/ssi-sdk.agent-config": "0.32.1-next.54+3b988a2b",
62
+ "@sphereon/ssi-sdk-ext.did-resolver-jwk": "0.28.1-feature.esm.cjs.18",
63
+ "@sphereon/ssi-sdk.agent-config": "0.33.1-feature.jose.vcdm.55+6f02f6f8",
51
64
  "@types/i18n-js": "^3.8.9",
52
65
  "@types/lodash.memoize": "^4.1.9",
53
66
  "@types/sha.js": "^2.4.4",
54
67
  "@types/uuid": "^9.0.8",
68
+ "@veramo/data-store": "4.2.0",
55
69
  "@veramo/did-provider-key": "4.2.0",
56
70
  "@veramo/did-resolver": "4.2.0",
57
71
  "@veramo/remote-client": "4.2.0",
58
72
  "@veramo/remote-server": "4.2.0",
59
73
  "@veramo/utils": "4.2.0",
60
74
  "did-resolver": "^4.1.0",
61
- "nock": "^13.5.4"
75
+ "nock": "^13.5.4",
76
+ "typeorm": "0.3.20"
62
77
  },
63
78
  "files": [
64
- "dist/**/*",
65
- "src/**/*",
79
+ "dist",
80
+ "src",
66
81
  "src/localization/translations/*",
67
82
  "README.md",
68
83
  "plugin.schema.json",
@@ -87,6 +102,5 @@
87
102
  "OpenID Connect",
88
103
  "Authenticator"
89
104
  ],
90
- "nx": {},
91
- "gitHead": "3b988a2bb62a7c4534a2670ea3a0985fd93d00f2"
105
+ "gitHead": "6f02f6f83679198268c6e1ea956be24cc1017234"
92
106
  }
@@ -2,44 +2,46 @@ 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'
16
+
17
+ import { OpSession } from '../session'
18
+ import { PEX, Status } from '@sphereon/pex'
19
+ import { computeEntryHash } from '@veramo/utils'
20
+ import { UniqueDigitalCredential } from '@sphereon/ssi-sdk.credential-store'
21
+ import { EventEmitter } from 'events'
14
22
  import {
15
23
  DidAuthSiopOpAuthenticatorOptions,
16
24
  GetSelectableCredentialsArgs,
25
+ IDidAuthSiopOpAuthenticator,
26
+ IGetSiopSessionArgs,
17
27
  IOpSessionArgs,
28
+ IRegisterCustomApprovalForSiopArgs,
29
+ IRemoveCustomApprovalForSiopArgs,
30
+ IRemoveSiopSessionArgs,
31
+ IRequiredContext,
32
+ Json,
18
33
  LOGGER_NAMESPACE,
19
34
  RequiredContext,
20
- schema,
21
35
  SelectableCredentialsMap,
22
36
  Siopv2AuthorizationResponseData,
23
37
  VerifiableCredentialsWithDefinition,
24
- } from '../index'
25
- import { Siopv2Machine } from '../machine/Siopv2Machine'
26
- import { getSelectableCredentials, siopSendAuthorizationResponse, translateCorrelationIdToName } from '../services/Siopv2MachineService'
27
- import { OpSession } from '../session'
28
- import { PEX, Status } from '@sphereon/pex'
29
- import { computeEntryHash } from '@veramo/utils'
30
- import { UniqueDigitalCredential } from '@sphereon/ssi-sdk.credential-store'
31
- import { EventEmitter } from 'events'
38
+ } from '../types'
39
+
32
40
  import {
33
41
  AddIdentityArgs,
34
42
  CreateConfigArgs,
35
43
  CreateConfigResult,
36
44
  GetSiopRequestArgs,
37
- IDidAuthSiopOpAuthenticator,
38
- IGetSiopSessionArgs,
39
- IRegisterCustomApprovalForSiopArgs,
40
- IRemoveCustomApprovalForSiopArgs,
41
- IRemoveSiopSessionArgs,
42
- IRequiredContext,
43
45
  OnContactIdentityCreatedArgs,
44
46
  OnIdentifierCreatedArgs,
45
47
  RetrieveContactArgs,
@@ -47,8 +49,12 @@ import {
47
49
  Siopv2AuthorizationRequestData,
48
50
  Siopv2HolderEvent,
49
51
  Siopv2Machine as Siopv2MachineId,
50
- Siopv2MachineInstanceOpts
52
+ Siopv2MachineInstanceOpts,
51
53
  } from '../types'
54
+ import { DcqlCredential, DcqlPresentation, DcqlQuery, DcqlSdJwtVcCredential } from 'dcql'
55
+ import { Siopv2Machine } from '../machine/Siopv2Machine'
56
+ import { getSelectableCredentials, siopSendAuthorizationResponse, translateCorrelationIdToName } from '../services/Siopv2MachineService'
57
+ import { schema } from '..'
52
58
 
53
59
  const logger = Loggers.DEFAULT.options(LOGGER_NAMESPACE, {}).get(LOGGER_NAMESPACE)
54
60
 
@@ -84,22 +90,16 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
84
90
  siopGetSelectableCredentials: this.siopGetSelectableCredentials.bind(this),
85
91
  }
86
92
 
87
- private readonly hasher?: Hasher
88
93
  private readonly sessions: Map<string, OpSession>
89
94
  private readonly customApprovals: Record<string, (verifiedAuthorizationRequest: VerifiedAuthorizationRequest, sessionId: string) => Promise<void>>
90
95
  private readonly presentationSignCallback?: PresentationSignCallback
91
96
  private readonly onContactIdentityCreated?: (args: OnContactIdentityCreatedArgs) => Promise<void>
92
97
  private readonly onIdentifierCreated?: (args: OnIdentifierCreatedArgs) => Promise<void>
93
98
  private readonly eventEmitter?: EventEmitter
99
+ private readonly hasher?: HasherSync
94
100
 
95
101
  constructor(options?: DidAuthSiopOpAuthenticatorOptions) {
96
- const {
97
- onContactIdentityCreated,
98
- onIdentifierCreated,
99
- hasher,
100
- customApprovals = {},
101
- presentationSignCallback
102
- } = { ...options }
102
+ const { onContactIdentityCreated, onIdentifierCreated, hasher, customApprovals = {}, presentationSignCallback } = { ...options }
103
103
 
104
104
  this.hasher = hasher
105
105
  this.onContactIdentityCreated = onContactIdentityCreated
@@ -214,9 +214,14 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
214
214
  }
215
215
  const { sessionId, redirectUrl } = didAuthConfig
216
216
 
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 } }))
217
+ const session: OpSession = await agent.siopGetOPSession({ sessionId }).catch(
218
+ async () =>
219
+ await agent.siopRegisterOPSession({
220
+ requestJwtOrUri: redirectUrl,
221
+ sessionId,
222
+ op: { eventEmitter: this.eventEmitter, hasher: this.hasher },
223
+ }),
224
+ )
220
225
 
221
226
  logger.debug(`session: ${JSON.stringify(session.id, null, 2)}`)
222
227
  const verifiedAuthorizationRequest = await session.getAuthorizationRequest()
@@ -245,6 +250,7 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
245
250
  verifiedAuthorizationRequest.presentationDefinitions.length > 0)
246
251
  ? verifiedAuthorizationRequest.presentationDefinitions
247
252
  : undefined,
253
+ dcqlQuery: verifiedAuthorizationRequest.dcqlQuery,
248
254
  }
249
255
  }
250
256
 
@@ -345,32 +351,75 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
345
351
 
346
352
  const pex = new PEX({ hasher: this.hasher })
347
353
  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')
354
+ const dcqlCredentialsWithCredentials: Map<DcqlCredential, UniqueDigitalCredential> = new Map()
355
+
356
+ if (Array.isArray(authorizationRequestData.presentationDefinitions) && authorizationRequestData?.presentationDefinitions.length > 0) {
357
+ try {
358
+ authorizationRequestData.presentationDefinitions?.forEach((presentationDefinition) => {
359
+ const { areRequiredCredentialsPresent, verifiableCredential: verifiableCredentials } = pex.selectFrom(
360
+ presentationDefinition.definition,
361
+ selectedCredentials.map((udc) => udc.originalVerifiableCredential!),
362
+ )
363
+
364
+ if (areRequiredCredentialsPresent !== Status.ERROR && verifiableCredentials) {
365
+ let uniqueDigitalCredentials: UniqueDigitalCredential[] = []
366
+ uniqueDigitalCredentials = verifiableCredentials.map((vc) => {
367
+ // @ts-ignore FIXME Funke
368
+ const hash = typeof vc === 'string' ? computeEntryHash(vc.split('~'[0])) : computeEntryHash(vc)
369
+ const udc = selectedCredentials.find((udc) => udc.hash == hash || udc.originalVerifiableCredential == vc)
370
+
371
+ if (!udc) {
372
+ throw Error(
373
+ `UniqueDigitalCredential could not be found in store. Either the credential is not present in the store or the hash is not correct.`,
374
+ )
375
+ }
376
+ return udc
377
+ })
378
+ verifiableCredentialsWithDefinition.push({
379
+ definition: presentationDefinition,
380
+ credentials: uniqueDigitalCredentials,
381
+ })
362
382
  }
363
- return udc
364
- })
365
- verifiableCredentialsWithDefinition.push({
366
- definition: presentationDefinition,
367
- credentials: uniqueDigitalCredentials,
368
383
  })
384
+ } catch (e) {
385
+ return Promise.reject(e)
369
386
  }
370
- })
371
387
 
372
- if (verifiableCredentialsWithDefinition.length === 0) {
373
- return Promise.reject(Error('None of the selected credentials match any of the presentation definitions.'))
388
+ if (verifiableCredentialsWithDefinition.length === 0) {
389
+ return Promise.reject(Error('None of the selected credentials match any of the presentation definitions.'))
390
+ }
391
+ } else if (authorizationRequestData.dcqlQuery) {
392
+ //TODO Only SD-JWT and MSO MDOC are supported at the moment
393
+ if (this.hasMDocCredentials(selectedCredentials) || this.hasSdJwtCredentials(selectedCredentials)) {
394
+ try {
395
+ selectedCredentials.forEach((vc) => {
396
+ if (this.isSdJwtCredential(vc)) {
397
+ const payload = (vc.originalVerifiableCredential as SdJwtDecodedVerifiableCredential).decodedPayload
398
+ const result: DcqlSdJwtVcCredential = {
399
+ claims: payload as { [x: string]: Json },
400
+ vct: payload.vct,
401
+ credential_format: 'vc+sd-jwt',
402
+ }
403
+ dcqlCredentialsWithCredentials.set(result, vc)
404
+ //FIXME MDoc namespaces are incompatible: array of strings vs complex object - https://sphereon.atlassian.net/browse/SPRIND-143
405
+ } else {
406
+ throw Error(`Invalid credential format: ${vc.digitalCredential.documentFormat}`)
407
+ }
408
+ })
409
+ } catch (e) {
410
+ return Promise.reject(e)
411
+ }
412
+
413
+ const dcqlPresentationRecord: DcqlPresentation.Output = {}
414
+ const queryResult = DcqlQuery.query(authorizationRequestData.dcqlQuery, Array.from(dcqlCredentialsWithCredentials.keys()))
415
+ for (const [key, value] of Object.entries(queryResult.credential_matches)) {
416
+ if (value.success) {
417
+ dcqlPresentationRecord[key] = this.retrieveEncodedCredential(dcqlCredentialsWithCredentials.get(value.output)!) as
418
+ | string
419
+ | { [x: string]: Json }
420
+ }
421
+ }
422
+ }
374
423
  }
375
424
 
376
425
  const response = await siopSendAuthorizationResponse(
@@ -380,7 +429,7 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
380
429
  ...(args.idOpts && { idOpts: args.idOpts }),
381
430
  ...(authorizationRequestData.presentationDefinitions !== undefined && { verifiableCredentialsWithDefinition }),
382
431
  isFirstParty,
383
- hasher: this.hasher
432
+ hasher: this.hasher,
384
433
  },
385
434
  context,
386
435
  )
@@ -395,11 +444,41 @@ export class DidAuthSiopOpAuthenticator implements IAgentPlugin {
395
444
 
396
445
  return {
397
446
  body: responseBody,
398
- url: response.url,
399
- queryParams: decodeUriAsJson(response.url),
447
+ url: response?.url,
448
+ queryParams: decodeUriAsJson(response?.url),
400
449
  }
401
450
  }
402
451
 
452
+ private hasMDocCredentials = (credentials: UniqueDigitalCredential[]): boolean => {
453
+ return credentials.some(this.isMDocCredential)
454
+ }
455
+
456
+ private isMDocCredential = (credential: UniqueDigitalCredential) => {
457
+ return (
458
+ credential.digitalCredential.documentFormat === CredentialDocumentFormat.MSO_MDOC &&
459
+ credential.digitalCredential.documentType === DocumentType.VC
460
+ )
461
+ }
462
+
463
+ private hasSdJwtCredentials = (credentials: UniqueDigitalCredential[]): boolean => {
464
+ return credentials.some(this.isSdJwtCredential)
465
+ }
466
+
467
+ private isSdJwtCredential = (credential: UniqueDigitalCredential) => {
468
+ return (
469
+ credential.digitalCredential.documentFormat === CredentialDocumentFormat.SD_JWT && credential.digitalCredential.documentType === DocumentType.VC
470
+ )
471
+ }
472
+
473
+ private retrieveEncodedCredential = (credential: UniqueDigitalCredential) => {
474
+ return credential.originalVerifiableCredential !== undefined &&
475
+ credential.originalVerifiableCredential !== null &&
476
+ (credential?.originalVerifiableCredential as SdJwtDecodedVerifiableCredential)?.compactSdJwtVc !== undefined &&
477
+ (credential?.originalVerifiableCredential as SdJwtDecodedVerifiableCredential)?.compactSdJwtVc !== null
478
+ ? (credential.originalVerifiableCredential as SdJwtDecodedVerifiableCredential).compactSdJwtVc
479
+ : credential.originalVerifiableCredential
480
+ }
481
+
403
482
  private async siopGetSelectableCredentials(args: GetSelectableCredentialsArgs, context: RequiredContext): Promise<SelectableCredentialsMap> {
404
483
  const { authorizationRequestData } = args
405
484
 
@@ -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 }