@sphereon/ssi-sdk.oid4vci-holder 0.32.1-next.20 → 0.32.1-next.291

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 (68) hide show
  1. package/dist/agent/OID4VCIHolder.d.ts +1 -0
  2. package/dist/agent/OID4VCIHolder.d.ts.map +1 -1
  3. package/dist/agent/OID4VCIHolder.js +38 -24
  4. package/dist/agent/OID4VCIHolder.js.map +1 -1
  5. package/dist/index.d.ts +5 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +5 -2
  8. package/dist/index.js.map +1 -1
  9. package/dist/link-handler/index.d.ts +3 -2
  10. package/dist/link-handler/index.d.ts.map +1 -1
  11. package/dist/link-handler/index.js +2 -1
  12. package/dist/link-handler/index.js.map +1 -1
  13. package/dist/listeners/headlessStateNavListener.d.ts.map +1 -0
  14. package/dist/listeners/headlessStateNavListener.js.map +1 -0
  15. package/dist/localization/translations/en.json +5 -1
  16. package/dist/localization/translations/nl.json +5 -1
  17. package/dist/machines/firstPartyMachine.d.ts +15 -0
  18. package/dist/machines/firstPartyMachine.d.ts.map +1 -0
  19. package/dist/machines/firstPartyMachine.js +222 -0
  20. package/dist/machines/firstPartyMachine.js.map +1 -0
  21. package/dist/machines/oid4vciMachine.d.ts.map +1 -0
  22. package/dist/{machine → machines}/oid4vciMachine.js +58 -2
  23. package/dist/machines/oid4vciMachine.js.map +1 -0
  24. package/dist/mappers/OIDC4VCIBrandingMapper.d.ts.map +1 -0
  25. package/dist/{agent → mappers}/OIDC4VCIBrandingMapper.js +1 -1
  26. package/dist/mappers/OIDC4VCIBrandingMapper.js.map +1 -0
  27. package/dist/services/FirstPartyMachineServices.d.ts +9 -0
  28. package/dist/services/FirstPartyMachineServices.d.ts.map +1 -0
  29. package/dist/services/FirstPartyMachineServices.js +53 -0
  30. package/dist/services/FirstPartyMachineServices.js.map +1 -0
  31. package/dist/{agent → services}/OID4VCIHolderService.d.ts +3 -2
  32. package/dist/services/OID4VCIHolderService.d.ts.map +1 -0
  33. package/dist/{agent → services}/OID4VCIHolderService.js +49 -4
  34. package/dist/services/OID4VCIHolderService.js.map +1 -0
  35. package/dist/types/FirstPartyMachine.d.ts +112 -0
  36. package/dist/types/FirstPartyMachine.d.ts.map +1 -0
  37. package/dist/types/FirstPartyMachine.js +30 -0
  38. package/dist/types/FirstPartyMachine.js.map +1 -0
  39. package/dist/types/IOID4VCIHolder.d.ts +26 -15
  40. package/dist/types/IOID4VCIHolder.d.ts.map +1 -1
  41. package/dist/types/IOID4VCIHolder.js +3 -1
  42. package/dist/types/IOID4VCIHolder.js.map +1 -1
  43. package/package.json +26 -22
  44. package/src/agent/OID4VCIHolder.ts +48 -23
  45. package/src/index.ts +5 -2
  46. package/src/link-handler/index.ts +10 -5
  47. package/src/localization/translations/en.json +5 -1
  48. package/src/localization/translations/nl.json +5 -1
  49. package/src/machines/firstPartyMachine.ts +278 -0
  50. package/src/{machine → machines}/oid4vciMachine.ts +62 -3
  51. package/src/{agent → mappers}/OIDC4VCIBrandingMapper.ts +26 -25
  52. package/src/services/FirstPartyMachineServices.ts +64 -0
  53. package/src/{agent → services}/OID4VCIHolderService.ts +58 -10
  54. package/src/types/FirstPartyMachine.ts +161 -0
  55. package/src/types/IOID4VCIHolder.ts +52 -39
  56. package/dist/agent/OID4VCIHolderService.d.ts.map +0 -1
  57. package/dist/agent/OID4VCIHolderService.js.map +0 -1
  58. package/dist/agent/OIDC4VCIBrandingMapper.d.ts.map +0 -1
  59. package/dist/agent/OIDC4VCIBrandingMapper.js.map +0 -1
  60. package/dist/machine/headlessStateNavListener.d.ts.map +0 -1
  61. package/dist/machine/headlessStateNavListener.js.map +0 -1
  62. package/dist/machine/oid4vciMachine.d.ts.map +0 -1
  63. package/dist/machine/oid4vciMachine.js.map +0 -1
  64. /package/dist/{machine → listeners}/headlessStateNavListener.d.ts +0 -0
  65. /package/dist/{machine → listeners}/headlessStateNavListener.js +0 -0
  66. /package/dist/{machine → machines}/oid4vciMachine.d.ts +0 -0
  67. /package/dist/{agent → mappers}/OIDC4VCIBrandingMapper.d.ts +0 -0
  68. /package/src/{machine → listeners}/headlessStateNavListener.ts +0 -0
@@ -1,4 +1,4 @@
1
- import { AuthzFlowType, toAuthorizationResponsePayload } from '@sphereon/oid4vci-common'
1
+ import { AuthorizationChallengeCodeResponse, AuthzFlowType, toAuthorizationResponsePayload } from '@sphereon/oid4vci-common'
2
2
  import { IBasicIssuerLocaleBranding, Identity, IIssuerLocaleBranding, Party } from '@sphereon/ssi-sdk.data-store'
3
3
  import { assign, createMachine, DoneInvokeEvent, interpret } from 'xstate'
4
4
  import { translate } from '../localization/Localization'
@@ -29,6 +29,7 @@ import {
29
29
  SetAuthorizationCodeURLEvent,
30
30
  VerificationCodeEvent,
31
31
  } from '../types/IOID4VCIHolder'
32
+ import { FirstPartyMachineStateTypes } from '../types/FirstPartyMachine'
32
33
 
33
34
  const oid4vciHasNoContactGuard = (_ctx: OID4VCIMachineContext, _event: OID4VCIMachineEventTypes): boolean => {
34
35
  const { contact } = _ctx
@@ -117,6 +118,10 @@ const oid4vciHasAuthorizationResponse = (ctx: OID4VCIMachineContext, _event: OID
117
118
  return !!ctx.openID4VCIClientState?.authorizationCodeResponse
118
119
  }
119
120
 
121
+ const oid4vciIsFirstPartyApplication = (ctx: OID4VCIMachineContext, _event: OID4VCIMachineEventTypes): boolean => {
122
+ return !!ctx.serverMetadata?.authorization_challenge_endpoint
123
+ }
124
+
120
125
  const createOID4VCIMachine = (opts?: CreateOID4VCIMachineOpts): OID4VCIStateMachine => {
121
126
  const initialContext: OID4VCIMachineContext = {
122
127
  // TODO WAL-671 we need to store the data from OpenIdProvider here in the context and make sure we can restart the machine with it and init the OpenIdProvider
@@ -153,7 +158,8 @@ const createOID4VCIMachine = (opts?: CreateOID4VCIMachineOpts): OID4VCIStateMach
153
158
  | { type: OID4VCIMachineGuards.hasSelectedCredentialsGuard }
154
159
  | { type: OID4VCIMachineGuards.hasAuthorizationResponse }
155
160
  | { type: OID4VCIMachineGuards.isOIDFOriginGuard }
156
- | { type: OID4VCIMachineGuards.contactHasLowTrustGuard },
161
+ | { type: OID4VCIMachineGuards.contactHasLowTrustGuard }
162
+ | { type: OID4VCIMachineGuards.isFirstPartyApplication },
157
163
  services: {} as {
158
164
  [OID4VCIMachineServices.start]: {
159
165
  data: StartResult
@@ -188,6 +194,9 @@ const createOID4VCIMachine = (opts?: CreateOID4VCIMachineOpts): OID4VCIStateMach
188
194
  [OID4VCIMachineServices.getIssuerBranding]: {
189
195
  data: Array<IIssuerLocaleBranding | IBasicIssuerLocaleBranding>
190
196
  }
197
+ [OID4VCIMachineServices.startFirstPartApplicationFlow]: {
198
+ data: void
199
+ }
191
200
  },
192
201
  },
193
202
  context: initialContext,
@@ -332,6 +341,10 @@ const createOID4VCIMachine = (opts?: CreateOID4VCIMachineOpts): OID4VCIStateMach
332
341
  target: OID4VCIMachineStates.selectCredentials,
333
342
  cond: OID4VCIMachineGuards.credentialsToSelectRequiredGuard,
334
343
  },
344
+ {
345
+ target: OID4VCIMachineStates.startFirstPartApplicationFlow,
346
+ cond: OID4VCIMachineGuards.isFirstPartyApplication,
347
+ },
335
348
  {
336
349
  target: OID4VCIMachineStates.initiateAuthorizationRequest,
337
350
  cond: OID4VCIMachineGuards.requireAuthorizationGuard,
@@ -422,6 +435,10 @@ const createOID4VCIMachine = (opts?: CreateOID4VCIMachineOpts): OID4VCIStateMach
422
435
  target: OID4VCIMachineStates.selectCredentials,
423
436
  cond: OID4VCIMachineGuards.credentialsToSelectRequiredGuard,
424
437
  },
438
+ {
439
+ target: OID4VCIMachineStates.startFirstPartApplicationFlow,
440
+ cond: OID4VCIMachineGuards.isFirstPartyApplication,
441
+ },
425
442
  {
426
443
  target: OID4VCIMachineStates.initiateAuthorizationRequest,
427
444
  cond: OID4VCIMachineGuards.requireAuthorizationGuard,
@@ -435,6 +452,43 @@ const createOID4VCIMachine = (opts?: CreateOID4VCIMachineOpts): OID4VCIStateMach
435
452
  },
436
453
  ],
437
454
  },
455
+ [OID4VCIMachineStates.startFirstPartApplicationFlow]: {
456
+ id: OID4VCIMachineStates.startFirstPartApplicationFlow,
457
+ invoke: {
458
+ src: OID4VCIMachineServices.startFirstPartApplicationFlow,
459
+ onDone: [
460
+ {
461
+ target: OID4VCIMachineStates.aborted,
462
+ cond: (_ctx: OID4VCIMachineContext, _event: DoneInvokeEvent<FirstPartyMachineStateTypes>): boolean =>
463
+ _event.data === FirstPartyMachineStateTypes.aborted,
464
+ },
465
+ {
466
+ target: OID4VCIMachineStates.declined,
467
+ cond: (_ctx: OID4VCIMachineContext, _event: DoneInvokeEvent<FirstPartyMachineStateTypes>): boolean =>
468
+ _event.data === FirstPartyMachineStateTypes.declined,
469
+ },
470
+ {
471
+ target: OID4VCIMachineStates.getCredentials,
472
+ actions: assign({
473
+ openID4VCIClientState: (_ctx: OID4VCIMachineContext, _event: DoneInvokeEvent<AuthorizationChallengeCodeResponse>) => {
474
+ const authorizationCodeResponse = toAuthorizationResponsePayload(_event.data)
475
+ return { ..._ctx.openID4VCIClientState!, authorizationCodeResponse }
476
+ },
477
+ }),
478
+ },
479
+ ],
480
+ onError: {
481
+ target: OID4VCIMachineStates.handleError,
482
+ actions: assign({
483
+ error: (_ctx: OID4VCIMachineContext, _event: DoneInvokeEvent<ErrorDetails>): ErrorDetails => ({
484
+ title: _event.data.title ?? translate('oid4vci_machine_first_party_error_title'),
485
+ message: _event.data.message,
486
+ stack: _event.data.stack,
487
+ }),
488
+ }),
489
+ },
490
+ },
491
+ },
438
492
  [OID4VCIMachineStates.selectCredentials]: {
439
493
  id: OID4VCIMachineStates.selectCredentials,
440
494
  on: {
@@ -453,6 +507,10 @@ const createOID4VCIMachine = (opts?: CreateOID4VCIMachineOpts): OID4VCIStateMach
453
507
  [OID4VCIMachineStates.transitionFromSelectingCredentials]: {
454
508
  id: OID4VCIMachineStates.transitionFromSelectingCredentials,
455
509
  always: [
510
+ {
511
+ target: OID4VCIMachineStates.startFirstPartApplicationFlow,
512
+ cond: OID4VCIMachineGuards.isFirstPartyApplication,
513
+ },
456
514
  {
457
515
  target: OID4VCIMachineStates.verifyPin,
458
516
  cond: OID4VCIMachineGuards.requirePinGuard,
@@ -726,6 +784,7 @@ export class OID4VCIMachine {
726
784
  oid4vciHasAuthorizationResponse,
727
785
  oid4vciIsOIDFOriginGuard,
728
786
  oid4vciContactHasLowTrustGuard,
787
+ oid4vciIsFirstPartyApplication,
729
788
  ...opts?.guards,
730
789
  },
731
790
  }),
@@ -737,7 +796,7 @@ export class OID4VCIMachine {
737
796
  if (opts?.requireCustomNavigationHook !== true) {
738
797
  if (typeof opts?.stateNavigationListener === 'function') {
739
798
  interpreter.onTransition((snapshot: OID4VCIMachineState): void => {
740
- if (opts?.stateNavigationListener !== undefined) {
799
+ if (opts?.stateNavigationListener) {
741
800
  opts.stateNavigationListener(interpreter, snapshot)
742
801
  }
743
802
  })
@@ -1,15 +1,6 @@
1
1
  import { CredentialsSupportedDisplay, NameAndLocale } from '@sphereon/oid4vci-common'
2
- import {
3
- IBasicCredentialClaim,
4
- IBasicCredentialLocaleBranding,
5
- IBasicIssuerLocaleBranding
6
- } from '@sphereon/ssi-sdk.data-store'
7
- import {
8
- SdJwtClaimDisplayMetadata,
9
- SdJwtClaimMetadata,
10
- SdJwtClaimPath,
11
- SdJwtTypeDisplayMetadata
12
- } from '@sphereon/ssi-types'
2
+ import { IBasicCredentialClaim, IBasicCredentialLocaleBranding, IBasicIssuerLocaleBranding } from '@sphereon/ssi-sdk.data-store'
3
+ import { SdJwtClaimDisplayMetadata, SdJwtClaimMetadata, SdJwtClaimPath, SdJwtTypeDisplayMetadata } from '@sphereon/ssi-types'
13
4
  import {
14
5
  IssuerLocaleBrandingFromArgs,
15
6
  Oid4vciCombineDisplayLocalesFromArgs,
@@ -26,7 +17,9 @@ import {
26
17
 
27
18
  // FIXME should we not move this to the branding plugin?
28
19
 
29
- export const oid4vciGetCredentialBrandingFrom = async (args: Oid4vciGetCredentialBrandingFromArgs): Promise<Array<IBasicCredentialLocaleBranding>> => {
20
+ export const oid4vciGetCredentialBrandingFrom = async (
21
+ args: Oid4vciGetCredentialBrandingFromArgs,
22
+ ): Promise<Array<IBasicCredentialLocaleBranding>> => {
30
23
  const { credentialDisplay, issuerCredentialSubject } = args
31
24
 
32
25
  return oid4vciCombineDisplayLocalesFrom({
@@ -35,7 +28,9 @@ export const oid4vciGetCredentialBrandingFrom = async (args: Oid4vciGetCredentia
35
28
  })
36
29
  }
37
30
 
38
- export const oid4vciCredentialDisplayLocalesFrom = async (args: Oid4vciCredentialDisplayLocalesFromArgs): Promise<Map<string, CredentialsSupportedDisplay>> => {
31
+ export const oid4vciCredentialDisplayLocalesFrom = async (
32
+ args: Oid4vciCredentialDisplayLocalesFromArgs,
33
+ ): Promise<Map<string, CredentialsSupportedDisplay>> => {
39
34
  const { credentialDisplay } = args
40
35
  return credentialDisplay.reduce((localeDisplays, display) => {
41
36
  const localeKey = display.locale || ''
@@ -44,7 +39,9 @@ export const oid4vciCredentialDisplayLocalesFrom = async (args: Oid4vciCredentia
44
39
  }, new Map<string, CredentialsSupportedDisplay>())
45
40
  }
46
41
 
47
- export const oid4vciIssuerCredentialSubjectLocalesFrom = async (args: Oid4vciIssuerCredentialSubjectLocalesFromArgs): Promise<Map<string, Array<IBasicCredentialClaim>>> => {
42
+ export const oid4vciIssuerCredentialSubjectLocalesFrom = async (
43
+ args: Oid4vciIssuerCredentialSubjectLocalesFromArgs,
44
+ ): Promise<Map<string, Array<IBasicCredentialClaim>>> => {
48
45
  const { issuerCredentialSubject } = args
49
46
  const localeClaims = new Map<string, Array<IBasicCredentialClaim>>()
50
47
 
@@ -125,7 +122,9 @@ export const oid4vciCredentialLocaleBrandingFrom = async (args: Oid4vciCredentia
125
122
  }
126
123
  }
127
124
 
128
- export const oid4vciCombineDisplayLocalesFrom = async (args: Oid4vciCombineDisplayLocalesFromArgs): Promise<Array<IBasicCredentialLocaleBranding>> => {
125
+ export const oid4vciCombineDisplayLocalesFrom = async (
126
+ args: Oid4vciCombineDisplayLocalesFromArgs,
127
+ ): Promise<Array<IBasicCredentialLocaleBranding>> => {
129
128
  const {
130
129
  credentialDisplayLocales = new Map<string, CredentialsSupportedDisplay>(),
131
130
  issuerCredentialSubjectLocales = new Map<string, Array<IBasicCredentialClaim>>(),
@@ -156,7 +155,9 @@ export const sdJwtGetCredentialBrandingFrom = async (args: SdJwtGetCredentialBra
156
155
  })
157
156
  }
158
157
 
159
- export const sdJwtCredentialDisplayLocalesFrom = async (args: SdJwtCredentialDisplayLocalesFromArgs): Promise<Map<string, SdJwtTypeDisplayMetadata>> => {
158
+ export const sdJwtCredentialDisplayLocalesFrom = async (
159
+ args: SdJwtCredentialDisplayLocalesFromArgs,
160
+ ): Promise<Map<string, SdJwtTypeDisplayMetadata>> => {
160
161
  const { credentialDisplay } = args
161
162
  return credentialDisplay.reduce((localeDisplays, display) => {
162
163
  const localeKey = display.lang || ''
@@ -165,14 +166,16 @@ export const sdJwtCredentialDisplayLocalesFrom = async (args: SdJwtCredentialDis
165
166
  }, new Map<string, SdJwtTypeDisplayMetadata>())
166
167
  }
167
168
 
168
- export const sdJwtCredentialClaimLocalesFrom = async (args: SdJwtCredentialClaimLocalesFromArgs): Promise<Map<string, Array<IBasicCredentialClaim>>> => {
169
+ export const sdJwtCredentialClaimLocalesFrom = async (
170
+ args: SdJwtCredentialClaimLocalesFromArgs,
171
+ ): Promise<Map<string, Array<IBasicCredentialClaim>>> => {
169
172
  const { claimsMetadata } = args
170
173
  const localeClaims = new Map<string, Array<IBasicCredentialClaim>>()
171
174
 
172
175
  claimsMetadata.forEach((claim: SdJwtClaimMetadata): void => {
173
176
  claim.display?.forEach((display: SdJwtClaimDisplayMetadata): void => {
174
- const { lang = '', label } = display;
175
- const key = claim.path.map((value: SdJwtClaimPath) => String(value)).join('.');
177
+ const { lang = '', label } = display
178
+ const key = claim.path.map((value: SdJwtClaimPath) => String(value)).join('.')
176
179
  if (!localeClaims.has(lang)) {
177
180
  localeClaims.set(lang, [])
178
181
  }
@@ -180,7 +183,7 @@ export const sdJwtCredentialClaimLocalesFrom = async (args: SdJwtCredentialClaim
180
183
  })
181
184
  })
182
185
 
183
- return localeClaims;
186
+ return localeClaims
184
187
  }
185
188
 
186
189
  export const sdJwtCredentialLocaleBrandingFrom = async (args: SdJwtCredentialLocaleBrandingFromArgs): Promise<IBasicCredentialLocaleBranding> => {
@@ -213,17 +216,15 @@ export const sdJwtCredentialLocaleBrandingFrom = async (args: SdJwtCredentialLoc
213
216
  }),
214
217
  ...(credentialDisplay.rendering?.simple?.background_color && {
215
218
  background: {
216
- color: credentialDisplay.rendering.simple.background_color ,
219
+ color: credentialDisplay.rendering.simple.background_color,
217
220
  },
218
221
  }),
219
222
  }
220
223
  }
221
224
 
222
225
  export const sdJwtCombineDisplayLocalesFrom = async (args: SdJwtCombineDisplayLocalesFromArgs): Promise<Array<IBasicCredentialLocaleBranding>> => {
223
- const {
224
- credentialDisplayLocales = new Map<string, SdJwtTypeDisplayMetadata>(),
225
- claimsMetadata = new Map<string, Array<IBasicCredentialClaim>>(),
226
- } = args
226
+ const { credentialDisplayLocales = new Map<string, SdJwtTypeDisplayMetadata>(), claimsMetadata = new Map<string, Array<IBasicCredentialClaim>>() } =
227
+ args
227
228
 
228
229
  const locales: Array<string> = Array.from(new Set([...claimsMetadata.keys(), ...credentialDisplayLocales.keys()]))
229
230
 
@@ -0,0 +1,64 @@
1
+ import { OpenID4VCIClient } from '@sphereon/oid4vci-client'
2
+ import { AuthorizationChallengeValidationResponse } from '@sphereon/ssi-sdk.siopv2-oid4vp-common'
3
+ import { AuthorizationChallengeCodeResponse } from '@sphereon/oid4vci-common'
4
+ import { CreateConfigResult } from '@sphereon/ssi-sdk.siopv2-oid4vp-op-auth'
5
+ import { v4 as uuidv4 } from 'uuid'
6
+ import { RequiredContext } from '../types/IOID4VCIHolder'
7
+ import {
8
+ CreateConfigArgs,
9
+ GetSiopRequestArgs,
10
+ SendAuthorizationChallengeRequestArgs,
11
+ SendAuthorizationResponseArgs,
12
+ SiopV2AuthorizationRequestData,
13
+ } from '../types/FirstPartyMachine'
14
+
15
+ export const sendAuthorizationChallengeRequest = async (args: SendAuthorizationChallengeRequestArgs): Promise<AuthorizationChallengeCodeResponse> => {
16
+ const { openID4VCIClientState, authSession, presentationDuringIssuanceSession } = args
17
+
18
+ const oid4vciClient = await OpenID4VCIClient.fromState({ state: openID4VCIClientState })
19
+ return oid4vciClient.acquireAuthorizationChallengeCode({
20
+ clientId: oid4vciClient.clientId ?? uuidv4(),
21
+ ...(authSession && { authSession }),
22
+ ...(!authSession &&
23
+ openID4VCIClientState.credentialOffer?.preAuthorizedCode && { issuerState: openID4VCIClientState.credentialOffer?.preAuthorizedCode }),
24
+ ...(!authSession && openID4VCIClientState.credentialOffer?.issuerState && { issuerState: openID4VCIClientState.credentialOffer?.issuerState }),
25
+ ...(presentationDuringIssuanceSession && { presentationDuringIssuanceSession }),
26
+ })
27
+ }
28
+
29
+ export const createConfig = async (args: CreateConfigArgs, context: RequiredContext): Promise<CreateConfigResult> => {
30
+ const { presentationUri } = args
31
+
32
+ if (!presentationUri) {
33
+ return Promise.reject(Error('Missing presentation uri in context'))
34
+ }
35
+
36
+ return context.agent.siopCreateConfig({ url: presentationUri })
37
+ }
38
+
39
+ export const getSiopRequest = async (args: GetSiopRequestArgs, context: RequiredContext): Promise<SiopV2AuthorizationRequestData> => {
40
+ const { didAuthConfig, presentationUri } = args
41
+
42
+ if (presentationUri === undefined) {
43
+ return Promise.reject(Error('Missing presentation uri in context'))
44
+ }
45
+
46
+ if (didAuthConfig === undefined) {
47
+ return Promise.reject(Error('Missing did auth config in context'))
48
+ }
49
+
50
+ return context.agent.siopGetSiopRequest({ didAuthConfig, url: presentationUri })
51
+ }
52
+
53
+ export const sendAuthorizationResponse = async (args: SendAuthorizationResponseArgs, context: RequiredContext): Promise<string> => {
54
+ const { didAuthConfig, authorizationRequestData, selectedCredentials } = args
55
+
56
+ const responseData = await context.agent.siopSendResponse({
57
+ authorizationRequestData,
58
+ selectedCredentials,
59
+ didAuthConfig,
60
+ isFirstParty: true,
61
+ })
62
+
63
+ return (<AuthorizationChallengeValidationResponse>responseData.body).presentation_during_issuance_session
64
+ }
@@ -10,6 +10,7 @@ import {
10
10
  getTypesFromObject,
11
11
  MetadataDisplay,
12
12
  OpenId4VCIVersion,
13
+ AuthorizationChallengeCodeResponse,
13
14
  } from '@sphereon/oid4vci-common'
14
15
  import { KeyUse } from '@sphereon/ssi-sdk-ext.did-resolver-jwk'
15
16
  import { getOrCreatePrimaryIdentifier, SupportedDidMethodEnum } from '@sphereon/ssi-sdk-ext.did-utils'
@@ -25,6 +26,7 @@ import { keyTypeFromCryptographicSuite } from '@sphereon/ssi-sdk-ext.key-utils'
25
26
  import { IBasicCredentialLocaleBranding, IBasicIssuerLocaleBranding } from '@sphereon/ssi-sdk.data-store'
26
27
  import {
27
28
  CredentialMapper,
29
+ Hasher,
28
30
  IVerifiableCredential,
29
31
  JoseSignatureAlgorithm,
30
32
  JoseSignatureAlgorithmString,
@@ -56,12 +58,13 @@ import {
56
58
  SelectAppLocaleBrandingArgs,
57
59
  VerificationResult,
58
60
  VerifyCredentialToAcceptArgs,
61
+ StartFirstPartApplicationMachine,
62
+ RequiredContext,
59
63
  } from '../types/IOID4VCIHolder'
60
- import {
61
- oid4vciGetCredentialBrandingFrom,
62
- sdJwtGetCredentialBrandingFrom,
63
- issuerLocaleBrandingFrom
64
- } from './OIDC4VCIBrandingMapper'
64
+ import { oid4vciGetCredentialBrandingFrom, sdJwtGetCredentialBrandingFrom, issuerLocaleBrandingFrom } from '../mappers/OIDC4VCIBrandingMapper'
65
+ import { FirstPartyMachine } from '../machines/firstPartyMachine'
66
+ import { FirstPartyMachineState, FirstPartyMachineStateTypes } from '../types/FirstPartyMachine'
67
+ import { defaultHasher } from '@sphereon/ssi-sdk.core'
65
68
 
66
69
  export const getCredentialBranding = async (args: GetCredentialBrandingArgs): Promise<Record<string, Array<IBasicCredentialLocaleBranding>>> => {
67
70
  const { credentialsSupported, context } = args
@@ -83,14 +86,14 @@ export const getCredentialBranding = async (args: GetCredentialBrandingArgs): Pr
83
86
  if (sdJwtTypeMetadata) {
84
87
  mappedLocaleBranding = await sdJwtGetCredentialBrandingFrom({
85
88
  credentialDisplay: sdJwtTypeMetadata.display,
86
- claimsMetadata: sdJwtTypeMetadata.claims
89
+ claimsMetadata: sdJwtTypeMetadata.claims,
87
90
  })
88
91
  } else {
89
92
  mappedLocaleBranding = await oid4vciGetCredentialBrandingFrom({
90
93
  credentialDisplay: credentialsConfigSupported.display,
91
94
  issuerCredentialSubject:
92
- // @ts-ignore // FIXME SPRIND-123 add proper support for type recognition as claim display can be located elsewhere for v13
93
- credentialsSupported.claims !== undefined ? credentialsConfigSupported.claims : credentialsConfigSupported.credentialSubject,
95
+ // @ts-ignore // FIXME SPRIND-123 add proper support for type recognition as claim display can be located elsewhere for v13
96
+ credentialsSupported.claims !== undefined ? credentialsConfigSupported.claims : credentialsConfigSupported.credentialSubject,
94
97
  })
95
98
  }
96
99
  // TODO we should make the mapper part of the plugin, so that the logic for getting the branding becomes more clear and easier to use
@@ -155,7 +158,7 @@ export const verifyCredentialToAccept = async (args: VerifyCredentialToAcceptArg
155
158
  return Promise.reject(Error('No credential found in credential response'))
156
159
  }
157
160
 
158
- const wrappedVC = CredentialMapper.toWrappedVerifiableCredential(credential, { hasher })
161
+ const wrappedVC = CredentialMapper.toWrappedVerifiableCredential(credential, { hasher: hasher ?? defaultHasher })
159
162
  if (
160
163
  wrappedVC.decoded?.iss?.includes('did:ebsi:') ||
161
164
  (typeof wrappedVC.decoded?.vc?.issuer === 'string'
@@ -221,7 +224,7 @@ export const mapCredentialToAccept = async (args: MapCredentialToAcceptArgs): Pr
221
224
  if (!hasher) {
222
225
  return Promise.reject('a hasher is required for encoded SD-JWT credentials')
223
226
  }
224
- const asyncHasher = (data: string, algorithm: string) => Promise.resolve(hasher(data, algorithm))
227
+ const asyncHasher: Hasher = (data: string | ArrayBuffer, algorithm: string) => Promise.resolve(hasher(data, algorithm))
225
228
  const decodedSdJwt = await CredentialMapper.decodeSdJwtVcAsync(wrappedVerifiableCredential.credential, asyncHasher)
226
229
  uniformVerifiableCredential = sdJwtDecodedCredentialToUniformCredential(<SdJwtDecodedVerifiableCredential>decodedSdJwt)
227
230
  } else if (CredentialMapper.isMsoMdocDecodedCredential(wrappedVerifiableCredential.credential)) {
@@ -615,3 +618,48 @@ export const getIssuanceCryptoSuite = async (opts: GetIssuanceCryptoSuiteArgs):
615
618
  return Promise.reject(Error(`Credential format '${credentialSupported.format}' not supported`))
616
619
  }
617
620
  }
621
+
622
+ export const startFirstPartApplicationMachine = async (
623
+ args: StartFirstPartApplicationMachine,
624
+ context: RequiredContext,
625
+ ): Promise<AuthorizationChallengeCodeResponse | string> => {
626
+ const { openID4VCIClientState, stateNavigationListener, contact } = args
627
+
628
+ if (!openID4VCIClientState) {
629
+ return Promise.reject(Error('Missing openID4VCI client state in context'))
630
+ }
631
+
632
+ if (!contact) {
633
+ return Promise.reject(Error('Missing contact in context'))
634
+ }
635
+
636
+ const firstPartyMachineInstance = FirstPartyMachine.newInstance({
637
+ openID4VCIClientState,
638
+ contact,
639
+ agentContext: context,
640
+ stateNavigationListener,
641
+ })
642
+
643
+ return new Promise((resolve, reject) => {
644
+ try {
645
+ firstPartyMachineInstance.onTransition((state: FirstPartyMachineState) => {
646
+ if (state.matches(FirstPartyMachineStateTypes.done)) {
647
+ const authorizationCodeResponse = state.context.authorizationCodeResponse
648
+ if (!authorizationCodeResponse) {
649
+ reject(Error('No authorizationCodeResponse acquired'))
650
+ }
651
+ resolve(authorizationCodeResponse!)
652
+ } else if (state.matches(FirstPartyMachineStateTypes.aborted)) {
653
+ resolve(FirstPartyMachineStateTypes.aborted)
654
+ } else if (state.matches(FirstPartyMachineStateTypes.declined)) {
655
+ resolve(FirstPartyMachineStateTypes.declined)
656
+ } else if (state.matches(FirstPartyMachineStateTypes.error)) {
657
+ reject(state.context.error)
658
+ }
659
+ })
660
+ firstPartyMachineInstance.start()
661
+ } catch (error) {
662
+ reject(error)
663
+ }
664
+ })
665
+ }
@@ -0,0 +1,161 @@
1
+ import { BaseActionObject, Interpreter, ResolveTypegenMeta, ServiceMap, State, StateMachine, StatesConfig, TypegenDisabled } from 'xstate'
2
+ import { OpenID4VCIClientState } from '@sphereon/oid4vci-client'
3
+ import { DidAuthConfig, Party } from '@sphereon/ssi-sdk.data-store'
4
+ import { PresentationDefinitionWithLocation, RPRegistrationMetadataPayload } from '@sphereon/did-auth-siop'
5
+ import { UniqueDigitalCredential } from '@sphereon/ssi-sdk.credential-store'
6
+ import { AuthorizationChallengeCodeResponse } from '@sphereon/oid4vci-common'
7
+ import { IIdentifier } from '@veramo/core'
8
+ import { ErrorDetails, RequiredContext } from './IOID4VCIHolder'
9
+
10
+ export enum FirstPartyMachineStateTypes {
11
+ sendAuthorizationChallengeRequest = 'sendAuthorizationChallengeRequest',
12
+ sendAuthorizationResponse = 'sendAuthorizationResponse',
13
+ selectCredentials = 'selectCredentials',
14
+ createConfig = 'createConfig',
15
+ getSiopRequest = 'getSiopRequest',
16
+ error = 'error',
17
+ done = 'done',
18
+ aborted = 'aborted',
19
+ declined = 'declined',
20
+ }
21
+
22
+ export enum FirstPartyMachineServices {
23
+ sendAuthorizationChallengeRequest = 'sendAuthorizationChallengeRequest',
24
+ sendAuthorizationResponse = 'sendAuthorizationResponse',
25
+ createConfig = 'createConfig',
26
+ getSiopRequest = 'getSiopRequest',
27
+ }
28
+
29
+ export type FirstPartyMachineStates = Record<FirstPartyMachineStateTypes, {}>
30
+
31
+ export type FirstPartyMachineContext = {
32
+ openID4VCIClientState: OpenID4VCIClientState
33
+ selectedCredentials: Array<UniqueDigitalCredential>
34
+ contact: Party
35
+ authSession?: string
36
+ presentationUri?: string
37
+ identifier?: IIdentifier
38
+ didAuthConfig?: Omit<DidAuthConfig, 'identifier'>
39
+ authorizationRequestData?: SiopV2AuthorizationRequestData
40
+ presentationDuringIssuanceSession?: string
41
+ authorizationCodeResponse?: AuthorizationChallengeCodeResponse
42
+ error?: ErrorDetails
43
+ }
44
+
45
+ export enum FirstPartyMachineEvents {
46
+ NEXT = 'NEXT',
47
+ PREVIOUS = 'PREVIOUS',
48
+ DECLINE = 'DECLINE',
49
+ SET_SELECTED_CREDENTIALS = 'SET_SELECTED_CREDENTIALS',
50
+ }
51
+
52
+ export type FirstPartyNextEvent = { type: FirstPartyMachineEvents.NEXT }
53
+ export type FirstPartyPreviousEvent = { type: FirstPartyMachineEvents.PREVIOUS }
54
+ export type FirstPartyDeclineEvent = { type: FirstPartyMachineEvents.DECLINE }
55
+ export type FirstPartySelectCredentialsEvent = {
56
+ type: FirstPartyMachineEvents.SET_SELECTED_CREDENTIALS
57
+ data: Array<UniqueDigitalCredential>
58
+ }
59
+
60
+ export type FirstPartyMachineEventTypes = FirstPartyNextEvent | FirstPartyPreviousEvent | FirstPartyDeclineEvent | FirstPartySelectCredentialsEvent
61
+
62
+ export type FirstPartyMachineStatesConfig = StatesConfig<
63
+ FirstPartyMachineContext,
64
+ {
65
+ states: FirstPartyMachineStates
66
+ },
67
+ FirstPartyMachineEventTypes,
68
+ any
69
+ >
70
+
71
+ export type CreateFirstPartyMachineOpts = {
72
+ openID4VCIClientState: OpenID4VCIClientState
73
+ contact: Party
74
+ agentContext: RequiredContext
75
+ machineId?: string
76
+ }
77
+
78
+ export type FirstPartyStateMachine = StateMachine<
79
+ FirstPartyMachineContext,
80
+ any,
81
+ FirstPartyMachineEventTypes,
82
+ {
83
+ value: any
84
+ context: FirstPartyMachineContext
85
+ },
86
+ BaseActionObject,
87
+ ServiceMap,
88
+ ResolveTypegenMeta<TypegenDisabled, FirstPartyMachineEventTypes, BaseActionObject, ServiceMap>
89
+ >
90
+
91
+ export type FirstPartyMachineInterpreter = Interpreter<
92
+ FirstPartyMachineContext,
93
+ any,
94
+ FirstPartyMachineEventTypes,
95
+ {
96
+ value: any
97
+ context: FirstPartyMachineContext
98
+ },
99
+ any
100
+ >
101
+
102
+ export type FirstPartyMachineStateNavigationListener = (
103
+ firstPartyMachine: FirstPartyMachineInterpreter,
104
+ state: FirstPartyMachineState,
105
+ navigation?: any,
106
+ ) => Promise<void>
107
+
108
+ export type InstanceFirstPartyMachineOpts = {
109
+ services?: any
110
+ guards?: any
111
+ subscription?: () => void
112
+ requireCustomNavigationHook?: boolean
113
+ stateNavigationListener?: FirstPartyMachineStateNavigationListener
114
+ } & CreateFirstPartyMachineOpts
115
+
116
+ export type FirstPartyMachineState = State<
117
+ FirstPartyMachineContext,
118
+ FirstPartyMachineEventTypes,
119
+ any,
120
+ {
121
+ value: any
122
+ context: FirstPartyMachineContext
123
+ },
124
+ any
125
+ >
126
+
127
+ export type FirstPartyMachineServiceDefinitions = Record<keyof typeof FirstPartyMachineServices, (...args: Array<any>) => any>
128
+
129
+ export type SendAuthorizationChallengeRequestArgs = Pick<
130
+ FirstPartyMachineContext,
131
+ 'openID4VCIClientState' | 'authSession' | 'presentationDuringIssuanceSession'
132
+ >
133
+
134
+ export type SendAuthorizationResponseArgs = Pick<
135
+ FirstPartyMachineContext,
136
+ 'authSession' | 'presentationUri' | 'didAuthConfig' | 'authorizationRequestData' | 'selectedCredentials'
137
+ >
138
+
139
+ export type CreateConfigArgs = Pick<FirstPartyMachineContext, 'presentationUri' | 'identifier'>
140
+
141
+ export type GetSiopRequestArgs = Pick<FirstPartyMachineContext, 'didAuthConfig' | 'presentationUri'>
142
+
143
+ export type SiopV2AuthorizationRequestData = {
144
+ correlationId: string
145
+ registrationMetadataPayload: RPRegistrationMetadataPayload
146
+ issuer?: string
147
+ name?: string
148
+ uri?: URL
149
+ clientIdScheme?: string
150
+ clientId?: string
151
+ entityId?: string
152
+ presentationDefinitions?: PresentationDefinitionWithLocation[]
153
+ }
154
+
155
+ export type FirstPartyMachineNavigationArgs = {
156
+ firstPartyMachine: FirstPartyMachineInterpreter
157
+ state: FirstPartyMachineState
158
+ navigation: any
159
+ onNext?: () => void
160
+ onBack?: () => void
161
+ }