@sphereon/ssi-sdk.oid4vci-holder 0.32.1-feature.MWALL.715.49 → 0.32.1-feature.MWALL.715.state.persist.225

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 (67) hide show
  1. package/dist/agent/OID4VCIHolder.d.ts.map +1 -1
  2. package/dist/agent/OID4VCIHolder.js +20 -14
  3. package/dist/agent/OID4VCIHolder.js.map +1 -1
  4. package/dist/index.d.ts +5 -2
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +5 -2
  7. package/dist/index.js.map +1 -1
  8. package/dist/link-handler/index.d.ts +3 -2
  9. package/dist/link-handler/index.d.ts.map +1 -1
  10. package/dist/link-handler/index.js +2 -1
  11. package/dist/link-handler/index.js.map +1 -1
  12. package/dist/listeners/headlessStateNavListener.d.ts.map +1 -0
  13. package/dist/listeners/headlessStateNavListener.js.map +1 -0
  14. package/dist/localization/translations/en.json +5 -1
  15. package/dist/localization/translations/nl.json +5 -1
  16. package/dist/machines/firstPartyMachine.d.ts +15 -0
  17. package/dist/machines/firstPartyMachine.d.ts.map +1 -0
  18. package/dist/machines/firstPartyMachine.js +222 -0
  19. package/dist/machines/firstPartyMachine.js.map +1 -0
  20. package/dist/machines/oid4vciMachine.d.ts.map +1 -0
  21. package/dist/{machine → machines}/oid4vciMachine.js +58 -2
  22. package/dist/machines/oid4vciMachine.js.map +1 -0
  23. package/dist/mappers/OIDC4VCIBrandingMapper.d.ts.map +1 -0
  24. package/dist/mappers/OIDC4VCIBrandingMapper.js.map +1 -0
  25. package/dist/services/FirstPartyMachineServices.d.ts +9 -0
  26. package/dist/services/FirstPartyMachineServices.d.ts.map +1 -0
  27. package/dist/services/FirstPartyMachineServices.js +53 -0
  28. package/dist/services/FirstPartyMachineServices.js.map +1 -0
  29. package/dist/{agent → services}/OID4VCIHolderService.d.ts +3 -2
  30. package/dist/services/OID4VCIHolderService.d.ts.map +1 -0
  31. package/dist/{agent → services}/OID4VCIHolderService.js +46 -2
  32. package/dist/services/OID4VCIHolderService.js.map +1 -0
  33. package/dist/types/FirstPartyMachine.d.ts +112 -0
  34. package/dist/types/FirstPartyMachine.d.ts.map +1 -0
  35. package/dist/types/FirstPartyMachine.js +30 -0
  36. package/dist/types/FirstPartyMachine.js.map +1 -0
  37. package/dist/types/IOID4VCIHolder.d.ts +26 -15
  38. package/dist/types/IOID4VCIHolder.d.ts.map +1 -1
  39. package/dist/types/IOID4VCIHolder.js +3 -1
  40. package/dist/types/IOID4VCIHolder.js.map +1 -1
  41. package/package.json +25 -21
  42. package/src/agent/OID4VCIHolder.ts +33 -21
  43. package/src/index.ts +5 -2
  44. package/src/link-handler/index.ts +10 -5
  45. package/src/localization/translations/en.json +5 -1
  46. package/src/localization/translations/nl.json +5 -1
  47. package/src/machines/firstPartyMachine.ts +278 -0
  48. package/src/{machine → machines}/oid4vciMachine.ts +62 -3
  49. package/src/services/FirstPartyMachineServices.ts +64 -0
  50. package/src/{agent → services}/OID4VCIHolderService.ts +53 -2
  51. package/src/types/FirstPartyMachine.ts +161 -0
  52. package/src/types/IOID4VCIHolder.ts +51 -38
  53. package/dist/agent/OID4VCIHolderService.d.ts.map +0 -1
  54. package/dist/agent/OID4VCIHolderService.js.map +0 -1
  55. package/dist/agent/OIDC4VCIBrandingMapper.d.ts.map +0 -1
  56. package/dist/agent/OIDC4VCIBrandingMapper.js.map +0 -1
  57. package/dist/machine/headlessStateNavListener.d.ts.map +0 -1
  58. package/dist/machine/headlessStateNavListener.js.map +0 -1
  59. package/dist/machine/oid4vciMachine.d.ts.map +0 -1
  60. package/dist/machine/oid4vciMachine.js.map +0 -1
  61. /package/dist/{machine → listeners}/headlessStateNavListener.d.ts +0 -0
  62. /package/dist/{machine → listeners}/headlessStateNavListener.js +0 -0
  63. /package/dist/{machine → machines}/oid4vciMachine.d.ts +0 -0
  64. /package/dist/{agent → mappers}/OIDC4VCIBrandingMapper.d.ts +0 -0
  65. /package/dist/{agent → mappers}/OIDC4VCIBrandingMapper.js +0 -0
  66. /package/src/{machine → listeners}/headlessStateNavListener.ts +0 -0
  67. /package/src/{agent → mappers}/OIDC4VCIBrandingMapper.ts +0 -0
@@ -0,0 +1,278 @@
1
+ import { assign, createMachine, DoneInvokeEvent, interpret } from 'xstate'
2
+ import { AuthorizationChallengeCodeResponse, AuthorizationChallengeError, AuthorizationChallengeErrorResponse } from '@sphereon/oid4vci-common'
3
+ import { DidAuthConfig } from '@sphereon/ssi-sdk.data-store'
4
+ import { CreateConfigResult } from '@sphereon/ssi-sdk.siopv2-oid4vp-op-auth'
5
+ import { createConfig, getSiopRequest, sendAuthorizationChallengeRequest, sendAuthorizationResponse } from '../services/FirstPartyMachineServices'
6
+ import { translate } from '../localization/Localization'
7
+ import { ErrorDetails } from '../types/IOID4VCIHolder'
8
+ import {
9
+ CreateConfigArgs,
10
+ CreateFirstPartyMachineOpts,
11
+ FirstPartyMachineContext,
12
+ FirstPartyMachineEvents,
13
+ FirstPartyMachineEventTypes,
14
+ FirstPartyMachineInterpreter,
15
+ FirstPartyMachineServices,
16
+ FirstPartyMachineState,
17
+ FirstPartyMachineStatesConfig,
18
+ FirstPartyMachineStateTypes,
19
+ FirstPartyMachineServiceDefinitions,
20
+ FirstPartyStateMachine,
21
+ GetSiopRequestArgs,
22
+ InstanceFirstPartyMachineOpts,
23
+ SiopV2AuthorizationRequestData,
24
+ SendAuthorizationResponseArgs,
25
+ FirstPartySelectCredentialsEvent,
26
+ } from '../types/FirstPartyMachine'
27
+
28
+ const firstPartyMachineStates: FirstPartyMachineStatesConfig = {
29
+ [FirstPartyMachineStateTypes.sendAuthorizationChallengeRequest]: {
30
+ id: FirstPartyMachineStateTypes.sendAuthorizationChallengeRequest,
31
+ invoke: {
32
+ src: FirstPartyMachineServices.sendAuthorizationChallengeRequest,
33
+ onDone: {
34
+ target: FirstPartyMachineStateTypes.done,
35
+ actions: assign({
36
+ authorizationCodeResponse: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<AuthorizationChallengeCodeResponse>) => _event.data,
37
+ }),
38
+ },
39
+ onError: [
40
+ {
41
+ target: FirstPartyMachineStateTypes.createConfig,
42
+ cond: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<AuthorizationChallengeErrorResponse>): boolean =>
43
+ _event.data.error === AuthorizationChallengeError.insufficient_authorization,
44
+ actions: assign({
45
+ authSession: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<AuthorizationChallengeErrorResponse>) => _event.data.auth_session,
46
+ presentationUri: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<AuthorizationChallengeErrorResponse>) =>
47
+ _event.data.presentation,
48
+ }),
49
+ },
50
+ {
51
+ target: FirstPartyMachineStateTypes.error,
52
+ actions: assign({
53
+ error: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<Error>): ErrorDetails => ({
54
+ title: translate('oid4vci_machine_send_authorization_challenge_request_error_title'),
55
+ message: _event.data.message,
56
+ stack: _event.data.stack,
57
+ }),
58
+ }),
59
+ },
60
+ ],
61
+ },
62
+ },
63
+ [FirstPartyMachineStateTypes.createConfig]: {
64
+ id: FirstPartyMachineStateTypes.createConfig,
65
+ invoke: {
66
+ src: FirstPartyMachineServices.createConfig,
67
+ onDone: {
68
+ target: FirstPartyMachineStateTypes.getSiopRequest,
69
+ actions: assign({
70
+ didAuthConfig: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<DidAuthConfig>) => _event.data,
71
+ }),
72
+ },
73
+ onError: {
74
+ target: FirstPartyMachineStateTypes.error,
75
+ actions: assign({
76
+ error: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<Error>): ErrorDetails => ({
77
+ title: translate('oid4vci_machine_create_config_error_title'),
78
+ message: _event.data.message,
79
+ stack: _event.data.stack,
80
+ }),
81
+ }),
82
+ },
83
+ },
84
+ },
85
+ [FirstPartyMachineStateTypes.getSiopRequest]: {
86
+ id: FirstPartyMachineStateTypes.getSiopRequest,
87
+ invoke: {
88
+ src: FirstPartyMachineServices.getSiopRequest,
89
+ onDone: {
90
+ target: FirstPartyMachineStateTypes.selectCredentials,
91
+ actions: assign({
92
+ authorizationRequestData: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<SiopV2AuthorizationRequestData>) => _event.data,
93
+ }),
94
+ },
95
+ onError: {
96
+ target: FirstPartyMachineStateTypes.error,
97
+ actions: assign({
98
+ error: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<Error>): ErrorDetails => ({
99
+ title: translate('siopV2_machine_get_request_error_title'),
100
+ message: _event.data.message,
101
+ stack: _event.data.stack,
102
+ }),
103
+ }),
104
+ },
105
+ },
106
+ },
107
+ [FirstPartyMachineStateTypes.selectCredentials]: {
108
+ id: FirstPartyMachineStateTypes.selectCredentials,
109
+ on: {
110
+ [FirstPartyMachineEvents.SET_SELECTED_CREDENTIALS]: {
111
+ actions: assign({ selectedCredentials: (_ctx: FirstPartyMachineContext, _event: FirstPartySelectCredentialsEvent) => _event.data }),
112
+ },
113
+ [FirstPartyMachineEvents.NEXT]: {
114
+ target: FirstPartyMachineStateTypes.sendAuthorizationResponse,
115
+ },
116
+ [FirstPartyMachineEvents.DECLINE]: {
117
+ target: FirstPartyMachineStateTypes.declined,
118
+ },
119
+ [FirstPartyMachineEvents.PREVIOUS]: {
120
+ target: FirstPartyMachineStateTypes.aborted,
121
+ },
122
+ },
123
+ },
124
+ [FirstPartyMachineStateTypes.sendAuthorizationResponse]: {
125
+ id: FirstPartyMachineStateTypes.sendAuthorizationResponse,
126
+ invoke: {
127
+ src: FirstPartyMachineServices.sendAuthorizationResponse,
128
+ onDone: {
129
+ target: FirstPartyMachineStateTypes.sendAuthorizationChallengeRequest,
130
+ actions: assign({
131
+ presentationDuringIssuanceSession: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<string>) => _event.data,
132
+ }),
133
+ },
134
+ onError: {
135
+ target: FirstPartyMachineStateTypes.error,
136
+ actions: assign({
137
+ error: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<Error>): ErrorDetails => ({
138
+ title: translate('oid4vci_machine_get_request_error_title'),
139
+ message: _event.data.message,
140
+ stack: _event.data.stack,
141
+ }),
142
+ }),
143
+ },
144
+ },
145
+ },
146
+ [FirstPartyMachineStateTypes.aborted]: {
147
+ id: FirstPartyMachineStateTypes.aborted,
148
+ type: 'final',
149
+ },
150
+ [FirstPartyMachineStateTypes.declined]: {
151
+ id: FirstPartyMachineStateTypes.declined,
152
+ type: 'final',
153
+ },
154
+ [FirstPartyMachineStateTypes.error]: {
155
+ id: FirstPartyMachineStateTypes.error,
156
+ type: 'final',
157
+ },
158
+ [FirstPartyMachineStateTypes.done]: {
159
+ id: FirstPartyMachineStateTypes.done,
160
+ type: 'final',
161
+ },
162
+ }
163
+
164
+ const createFirstPartyActivationMachine = (opts: CreateFirstPartyMachineOpts): FirstPartyStateMachine => {
165
+ const initialContext: FirstPartyMachineContext = {
166
+ openID4VCIClientState: opts.openID4VCIClientState,
167
+ contact: opts.contact,
168
+ selectedCredentials: [],
169
+ }
170
+
171
+ return createMachine<FirstPartyMachineContext, FirstPartyMachineEventTypes>({
172
+ id: opts?.machineId ?? 'FirstParty',
173
+ predictableActionArguments: true,
174
+ initial: FirstPartyMachineStateTypes.sendAuthorizationChallengeRequest,
175
+ context: initialContext,
176
+ states: firstPartyMachineStates,
177
+ schema: {
178
+ events: {} as FirstPartyMachineEventTypes,
179
+ services: {} as {
180
+ [FirstPartyMachineServices.sendAuthorizationChallengeRequest]: {
181
+ data: void
182
+ }
183
+ [FirstPartyMachineServices.createConfig]: {
184
+ data: CreateConfigResult
185
+ }
186
+ [FirstPartyMachineServices.getSiopRequest]: {
187
+ data: SiopV2AuthorizationRequestData
188
+ }
189
+ [FirstPartyMachineServices.sendAuthorizationResponse]: {
190
+ data: string
191
+ }
192
+ },
193
+ },
194
+ })
195
+ }
196
+
197
+ export class FirstPartyMachine {
198
+ private static _instance: FirstPartyMachineInterpreter | undefined
199
+
200
+ static hasInstance(): boolean {
201
+ return FirstPartyMachine._instance !== undefined
202
+ }
203
+
204
+ static get instance(): FirstPartyMachineInterpreter {
205
+ if (!FirstPartyMachine._instance) {
206
+ throw Error('Please initialize ESIMActivation machine first')
207
+ }
208
+ return FirstPartyMachine._instance
209
+ }
210
+
211
+ static clearInstance(opts: { stop: boolean }) {
212
+ const { stop } = opts
213
+ if (FirstPartyMachine.hasInstance()) {
214
+ if (stop) {
215
+ FirstPartyMachine.stopInstance()
216
+ }
217
+ }
218
+ FirstPartyMachine._instance = undefined
219
+ }
220
+
221
+ static stopInstance(): void {
222
+ if (!FirstPartyMachine.hasInstance()) {
223
+ return
224
+ }
225
+ FirstPartyMachine.instance.stop()
226
+ FirstPartyMachine._instance = undefined
227
+ }
228
+
229
+ public static newInstance(opts: InstanceFirstPartyMachineOpts): FirstPartyMachineInterpreter {
230
+ const { agentContext } = opts
231
+ const services: FirstPartyMachineServiceDefinitions = {
232
+ [FirstPartyMachineServices.sendAuthorizationChallengeRequest]: sendAuthorizationChallengeRequest,
233
+ [FirstPartyMachineServices.createConfig]: (args: CreateConfigArgs) => createConfig(args, agentContext),
234
+ [FirstPartyMachineServices.getSiopRequest]: (args: GetSiopRequestArgs) => getSiopRequest(args, agentContext),
235
+ [FirstPartyMachineServices.sendAuthorizationResponse]: (args: SendAuthorizationResponseArgs) => sendAuthorizationResponse(args, agentContext),
236
+ }
237
+
238
+ const newInst: FirstPartyMachineInterpreter = interpret(
239
+ createFirstPartyActivationMachine(opts).withConfig({
240
+ services: {
241
+ ...services,
242
+ ...opts?.services,
243
+ },
244
+ guards: {
245
+ ...opts?.guards,
246
+ },
247
+ }),
248
+ )
249
+
250
+ if (typeof opts?.subscription === 'function') {
251
+ newInst.onTransition(opts.subscription)
252
+ }
253
+
254
+ if (opts?.requireCustomNavigationHook !== true) {
255
+ newInst.onTransition((snapshot: FirstPartyMachineState): void => {
256
+ if (opts?.stateNavigationListener) {
257
+ void opts.stateNavigationListener(newInst, snapshot)
258
+ }
259
+ })
260
+ }
261
+
262
+ return newInst
263
+ }
264
+
265
+ static getInstance(
266
+ opts: InstanceFirstPartyMachineOpts & {
267
+ requireExisting?: boolean
268
+ },
269
+ ): FirstPartyMachineInterpreter {
270
+ if (!FirstPartyMachine._instance) {
271
+ if (opts?.requireExisting === true) {
272
+ throw Error(`Existing FirstPartyMachine instance requested, but none was created at this point!`)
273
+ }
274
+ FirstPartyMachine._instance = FirstPartyMachine.newInstance(opts)
275
+ }
276
+ return FirstPartyMachine._instance
277
+ }
278
+ }
@@ -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
  })
@@ -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,8 +58,12 @@ import {
56
58
  SelectAppLocaleBrandingArgs,
57
59
  VerificationResult,
58
60
  VerifyCredentialToAcceptArgs,
61
+ StartFirstPartApplicationMachine,
62
+ RequiredContext,
59
63
  } from '../types/IOID4VCIHolder'
60
- import { oid4vciGetCredentialBrandingFrom, sdJwtGetCredentialBrandingFrom, issuerLocaleBrandingFrom } from './OIDC4VCIBrandingMapper'
64
+ import { oid4vciGetCredentialBrandingFrom, sdJwtGetCredentialBrandingFrom, issuerLocaleBrandingFrom } from '../mappers/OIDC4VCIBrandingMapper'
65
+ import { FirstPartyMachine } from '../machines/firstPartyMachine'
66
+ import { FirstPartyMachineState, FirstPartyMachineStateTypes } from '../types/FirstPartyMachine'
61
67
 
62
68
  export const getCredentialBranding = async (args: GetCredentialBrandingArgs): Promise<Record<string, Array<IBasicCredentialLocaleBranding>>> => {
63
69
  const { credentialsSupported, context } = args
@@ -217,7 +223,7 @@ export const mapCredentialToAccept = async (args: MapCredentialToAcceptArgs): Pr
217
223
  if (!hasher) {
218
224
  return Promise.reject('a hasher is required for encoded SD-JWT credentials')
219
225
  }
220
- const asyncHasher = (data: string, algorithm: string) => Promise.resolve(hasher(data, algorithm))
226
+ const asyncHasher: Hasher = (data: string, algorithm: string) => Promise.resolve(hasher(data, algorithm))
221
227
  const decodedSdJwt = await CredentialMapper.decodeSdJwtVcAsync(wrappedVerifiableCredential.credential, asyncHasher)
222
228
  uniformVerifiableCredential = sdJwtDecodedCredentialToUniformCredential(<SdJwtDecodedVerifiableCredential>decodedSdJwt)
223
229
  } else if (CredentialMapper.isMsoMdocDecodedCredential(wrappedVerifiableCredential.credential)) {
@@ -611,3 +617,48 @@ export const getIssuanceCryptoSuite = async (opts: GetIssuanceCryptoSuiteArgs):
611
617
  return Promise.reject(Error(`Credential format '${credentialSupported.format}' not supported`))
612
618
  }
613
619
  }
620
+
621
+ export const startFirstPartApplicationMachine = async (
622
+ args: StartFirstPartApplicationMachine,
623
+ context: RequiredContext,
624
+ ): Promise<AuthorizationChallengeCodeResponse | string> => {
625
+ const { openID4VCIClientState, stateNavigationListener, contact } = args
626
+
627
+ if (!openID4VCIClientState) {
628
+ return Promise.reject(Error('Missing openID4VCI client state in context'))
629
+ }
630
+
631
+ if (!contact) {
632
+ return Promise.reject(Error('Missing contact in context'))
633
+ }
634
+
635
+ const firstPartyMachineInstance = FirstPartyMachine.newInstance({
636
+ openID4VCIClientState,
637
+ contact,
638
+ agentContext: context,
639
+ stateNavigationListener,
640
+ })
641
+
642
+ return new Promise((resolve, reject) => {
643
+ try {
644
+ firstPartyMachineInstance.onTransition((state: FirstPartyMachineState) => {
645
+ if (state.matches(FirstPartyMachineStateTypes.done)) {
646
+ const authorizationCodeResponse = state.context.authorizationCodeResponse
647
+ if (!authorizationCodeResponse) {
648
+ reject(Error('No authorizationCodeResponse acquired'))
649
+ }
650
+ resolve(authorizationCodeResponse!)
651
+ } else if (state.matches(FirstPartyMachineStateTypes.aborted)) {
652
+ resolve(FirstPartyMachineStateTypes.aborted)
653
+ } else if (state.matches(FirstPartyMachineStateTypes.declined)) {
654
+ resolve(FirstPartyMachineStateTypes.declined)
655
+ } else if (state.matches(FirstPartyMachineStateTypes.error)) {
656
+ reject(state.context.error)
657
+ }
658
+ })
659
+ firstPartyMachineInstance.start()
660
+ } catch (error) {
661
+ reject(error)
662
+ }
663
+ })
664
+ }