@sphereon/ssi-sdk.oid4vci-holder 0.32.1-next.18 → 0.32.1-next.54

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 +15 -14
  3. package/dist/agent/OID4VCIHolder.js.map +1 -1
  4. package/dist/index.d.ts +4 -2
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +4 -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 +52 -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 +20 -9
  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 +19 -16
  42. package/src/agent/OID4VCIHolder.ts +23 -20
  43. package/src/index.ts +4 -2
  44. package/src/link-handler/index.ts +12 -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 +287 -0
  48. package/src/{machine → machines}/oid4vciMachine.ts +64 -3
  49. package/src/services/FirstPartyMachineServices.ts +63 -0
  50. package/src/{agent → services}/OID4VCIHolderService.ts +48 -1
  51. package/src/types/FirstPartyMachine.ts +169 -0
  52. package/src/types/IOID4VCIHolder.ts +40 -30
  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,287 @@
1
+ import { assign, createMachine, DoneInvokeEvent, interpret } from 'xstate'
2
+ import {
3
+ AuthorizationChallengeCodeResponse,
4
+ AuthorizationChallengeError,
5
+ AuthorizationChallengeErrorResponse
6
+ } from '@sphereon/oid4vci-common'
7
+ import { DidAuthConfig } from '@sphereon/ssi-sdk.data-store'
8
+ import { CreateConfigResult } from '@sphereon/ssi-sdk.siopv2-oid4vp-op-auth'
9
+ import {
10
+ createConfig,
11
+ getSiopRequest,
12
+ sendAuthorizationChallengeRequest,
13
+ sendAuthorizationResponse
14
+ } from '../services/FirstPartyMachineServices'
15
+ import { translate } from '../localization/Localization'
16
+ import { ErrorDetails } from '../types/IOID4VCIHolder'
17
+ import {
18
+ CreateConfigArgs,
19
+ CreateFirstPartyMachineOpts,
20
+ FirstPartyMachineContext,
21
+ FirstPartyMachineEvents,
22
+ FirstPartyMachineEventTypes,
23
+ FirstPartyMachineInterpreter,
24
+ FirstPartyMachineServices,
25
+ FirstPartyMachineState,
26
+ FirstPartyMachineStatesConfig,
27
+ FirstPartyMachineStateTypes,
28
+ FirstPartyMachineServiceDefinitions,
29
+ FirstPartyStateMachine,
30
+ GetSiopRequestArgs,
31
+ InstanceFirstPartyMachineOpts,
32
+ SiopV2AuthorizationRequestData,
33
+ SendAuthorizationResponseArgs,
34
+ FirstPartySelectCredentialsEvent
35
+ } from '../types/FirstPartyMachine'
36
+
37
+ const firstPartyMachineStates: FirstPartyMachineStatesConfig = {
38
+ [FirstPartyMachineStateTypes.sendAuthorizationChallengeRequest]: {
39
+ id: FirstPartyMachineStateTypes.sendAuthorizationChallengeRequest,
40
+ invoke: {
41
+ src: FirstPartyMachineServices.sendAuthorizationChallengeRequest,
42
+ onDone: {
43
+ target: FirstPartyMachineStateTypes.done,
44
+ actions: assign({
45
+ authorizationCodeResponse: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<AuthorizationChallengeCodeResponse>) => _event.data
46
+ })
47
+ },
48
+ onError: [
49
+ {
50
+ target: FirstPartyMachineStateTypes.createConfig,
51
+ cond: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<AuthorizationChallengeErrorResponse>): boolean => _event.data.error === AuthorizationChallengeError.insufficient_authorization,
52
+ actions: assign({
53
+ authSession: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<AuthorizationChallengeErrorResponse>) => _event.data.auth_session,
54
+ presentationUri: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<AuthorizationChallengeErrorResponse>) => _event.data.presentation,
55
+ }),
56
+ },
57
+ {
58
+ target: FirstPartyMachineStateTypes.error,
59
+ actions: assign({
60
+ error: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<Error>): ErrorDetails => ({
61
+ title: translate('oid4vci_machine_send_authorization_challenge_request_error_title'),
62
+ message: _event.data.message,
63
+ stack: _event.data.stack,
64
+ }),
65
+ }),
66
+ }
67
+ ],
68
+ }
69
+ },
70
+ [FirstPartyMachineStateTypes.createConfig]: {
71
+ id: FirstPartyMachineStateTypes.createConfig,
72
+ invoke: {
73
+ src: FirstPartyMachineServices.createConfig,
74
+ onDone: {
75
+ target: FirstPartyMachineStateTypes.getSiopRequest,
76
+ actions: assign({
77
+ didAuthConfig: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<DidAuthConfig>) => _event.data,
78
+ }),
79
+ },
80
+ onError: {
81
+ target: FirstPartyMachineStateTypes.error,
82
+ actions: assign({
83
+ error: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<Error>): ErrorDetails => ({
84
+ title: translate('oid4vci_machine_create_config_error_title'),
85
+ message: _event.data.message,
86
+ stack: _event.data.stack,
87
+ }),
88
+ }),
89
+ },
90
+ },
91
+ },
92
+ [FirstPartyMachineStateTypes.getSiopRequest]: {
93
+ id: FirstPartyMachineStateTypes.getSiopRequest,
94
+ invoke: {
95
+ src: FirstPartyMachineServices.getSiopRequest,
96
+ onDone: {
97
+ target: FirstPartyMachineStateTypes.selectCredentials,
98
+ actions: assign({
99
+ authorizationRequestData: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<SiopV2AuthorizationRequestData>) => _event.data,
100
+ }),
101
+ },
102
+ onError: {
103
+ target: FirstPartyMachineStateTypes.error,
104
+ actions: assign({
105
+ error: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<Error>): ErrorDetails => ({
106
+ title: translate('siopV2_machine_get_request_error_title'),
107
+ message: _event.data.message,
108
+ stack: _event.data.stack,
109
+ }),
110
+ }),
111
+ },
112
+ },
113
+ },
114
+ [FirstPartyMachineStateTypes.selectCredentials]: {
115
+ id: FirstPartyMachineStateTypes.selectCredentials,
116
+ on: {
117
+ [FirstPartyMachineEvents.SET_SELECTED_CREDENTIALS]: {
118
+ actions: assign({selectedCredentials: (_ctx: FirstPartyMachineContext, _event: FirstPartySelectCredentialsEvent) => _event.data}),
119
+ },
120
+ [FirstPartyMachineEvents.NEXT]: {
121
+ target: FirstPartyMachineStateTypes.sendAuthorizationResponse,
122
+ },
123
+ [FirstPartyMachineEvents.DECLINE]: {
124
+ target: FirstPartyMachineStateTypes.declined,
125
+ },
126
+ [FirstPartyMachineEvents.PREVIOUS]: {
127
+ target: FirstPartyMachineStateTypes.aborted,
128
+ },
129
+ },
130
+ },
131
+ [FirstPartyMachineStateTypes.sendAuthorizationResponse]: {
132
+ id: FirstPartyMachineStateTypes.sendAuthorizationResponse,
133
+ invoke: {
134
+ src: FirstPartyMachineServices.sendAuthorizationResponse,
135
+ onDone: {
136
+ target: FirstPartyMachineStateTypes.sendAuthorizationChallengeRequest,
137
+ actions: assign({
138
+ presentationDuringIssuanceSession: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<string>) => _event.data,
139
+ }),
140
+ },
141
+ onError: {
142
+ target: FirstPartyMachineStateTypes.error,
143
+ actions: assign({
144
+ error: (_ctx: FirstPartyMachineContext, _event: DoneInvokeEvent<Error>): ErrorDetails => ({
145
+ title: translate('oid4vci_machine_get_request_error_title'),
146
+ message: _event.data.message,
147
+ stack: _event.data.stack,
148
+ }),
149
+ }),
150
+ },
151
+ }
152
+ },
153
+ [FirstPartyMachineStateTypes.aborted]: {
154
+ id: FirstPartyMachineStateTypes.aborted,
155
+ type: 'final'
156
+ },
157
+ [FirstPartyMachineStateTypes.declined]: {
158
+ id: FirstPartyMachineStateTypes.declined,
159
+ type: 'final'
160
+ },
161
+ [FirstPartyMachineStateTypes.error]: {
162
+ id: FirstPartyMachineStateTypes.error,
163
+ type: 'final',
164
+ },
165
+ [FirstPartyMachineStateTypes.done]: {
166
+ id: FirstPartyMachineStateTypes.done,
167
+ type: 'final',
168
+ }
169
+ }
170
+
171
+ const createFirstPartyActivationMachine = (opts: CreateFirstPartyMachineOpts): FirstPartyStateMachine => {
172
+ const initialContext: FirstPartyMachineContext = {
173
+ openID4VCIClientState: opts.openID4VCIClientState,
174
+ contact: opts.contact,
175
+ selectedCredentials: [],
176
+ };
177
+
178
+ return createMachine<FirstPartyMachineContext, FirstPartyMachineEventTypes>(
179
+ {
180
+ id: opts?.machineId ?? 'FirstParty',
181
+ predictableActionArguments: true,
182
+ initial: FirstPartyMachineStateTypes.sendAuthorizationChallengeRequest,
183
+ context: initialContext,
184
+ states: firstPartyMachineStates,
185
+ schema: {
186
+ events: {} as FirstPartyMachineEventTypes,
187
+ services: {} as {
188
+ [FirstPartyMachineServices.sendAuthorizationChallengeRequest]: {
189
+ data: void
190
+ },
191
+ [FirstPartyMachineServices.createConfig]: {
192
+ data: CreateConfigResult
193
+ },
194
+ [FirstPartyMachineServices.getSiopRequest]: {
195
+ data: SiopV2AuthorizationRequestData
196
+ },
197
+ [FirstPartyMachineServices.sendAuthorizationResponse]: {
198
+ data: string
199
+ }
200
+ }
201
+ }
202
+ }
203
+ );
204
+ };
205
+
206
+ export class FirstPartyMachine {
207
+ private static _instance: FirstPartyMachineInterpreter | undefined;
208
+
209
+ static hasInstance(): boolean {
210
+ return FirstPartyMachine._instance !== undefined;
211
+ }
212
+
213
+ static get instance(): FirstPartyMachineInterpreter {
214
+ if (!FirstPartyMachine._instance) {
215
+ throw Error('Please initialize ESIMActivation machine first');
216
+ }
217
+ return FirstPartyMachine._instance;
218
+ }
219
+
220
+ static clearInstance(opts: {stop: boolean}) {
221
+ const {stop} = opts;
222
+ if (FirstPartyMachine.hasInstance()) {
223
+ if (stop) {
224
+ FirstPartyMachine.stopInstance();
225
+ }
226
+ }
227
+ FirstPartyMachine._instance = undefined;
228
+ }
229
+
230
+ static stopInstance(): void {
231
+ if (!FirstPartyMachine.hasInstance()) {
232
+ return;
233
+ }
234
+ FirstPartyMachine.instance.stop();
235
+ FirstPartyMachine._instance = undefined;
236
+ }
237
+
238
+ public static newInstance(opts: InstanceFirstPartyMachineOpts): FirstPartyMachineInterpreter {
239
+ const { agentContext } = opts
240
+ const services: FirstPartyMachineServiceDefinitions = {
241
+ [FirstPartyMachineServices.sendAuthorizationChallengeRequest]: sendAuthorizationChallengeRequest,
242
+ [FirstPartyMachineServices.createConfig]: (args: CreateConfigArgs) => createConfig(args, agentContext),
243
+ [FirstPartyMachineServices.getSiopRequest]: (args: GetSiopRequestArgs) => getSiopRequest(args, agentContext),
244
+ [FirstPartyMachineServices.sendAuthorizationResponse]: (args: SendAuthorizationResponseArgs) => sendAuthorizationResponse(args, agentContext),
245
+ }
246
+
247
+ const newInst: FirstPartyMachineInterpreter = interpret(
248
+ createFirstPartyActivationMachine(opts).withConfig({
249
+ services: {
250
+ ...services,
251
+ ...opts?.services,
252
+ },
253
+ guards: {
254
+ ...opts?.guards,
255
+ },
256
+ }),
257
+ );
258
+
259
+ if (typeof opts?.subscription === 'function') {
260
+ newInst.onTransition(opts.subscription);
261
+ }
262
+
263
+ if (opts?.requireCustomNavigationHook !== true) {
264
+ newInst.onTransition((snapshot: FirstPartyMachineState): void => {
265
+ if (opts?.stateNavigationListener) {
266
+ void opts.stateNavigationListener(newInst, snapshot)
267
+ }
268
+ });
269
+ }
270
+
271
+ return newInst;
272
+ }
273
+
274
+ static getInstance(
275
+ opts: InstanceFirstPartyMachineOpts & {
276
+ requireExisting?: boolean;
277
+ },
278
+ ): FirstPartyMachineInterpreter {
279
+ if (!FirstPartyMachine._instance) {
280
+ if (opts?.requireExisting === true) {
281
+ throw Error(`Existing FirstPartyMachine instance requested, but none was created at this point!`);
282
+ }
283
+ FirstPartyMachine._instance = FirstPartyMachine.newInstance(opts);
284
+ }
285
+ return FirstPartyMachine._instance;
286
+ }
287
+ }
@@ -1,4 +1,8 @@
1
- import { AuthzFlowType, toAuthorizationResponsePayload } from '@sphereon/oid4vci-common'
1
+ import {
2
+ AuthorizationChallengeCodeResponse,
3
+ AuthzFlowType,
4
+ toAuthorizationResponsePayload
5
+ } from '@sphereon/oid4vci-common'
2
6
  import { IBasicIssuerLocaleBranding, Identity, IIssuerLocaleBranding, Party } from '@sphereon/ssi-sdk.data-store'
3
7
  import { assign, createMachine, DoneInvokeEvent, interpret } from 'xstate'
4
8
  import { translate } from '../localization/Localization'
@@ -29,6 +33,7 @@ import {
29
33
  SetAuthorizationCodeURLEvent,
30
34
  VerificationCodeEvent,
31
35
  } from '../types/IOID4VCIHolder'
36
+ import { FirstPartyMachineStateTypes } from '../types/FirstPartyMachine'
32
37
 
33
38
  const oid4vciHasNoContactGuard = (_ctx: OID4VCIMachineContext, _event: OID4VCIMachineEventTypes): boolean => {
34
39
  const { contact } = _ctx
@@ -117,6 +122,10 @@ const oid4vciHasAuthorizationResponse = (ctx: OID4VCIMachineContext, _event: OID
117
122
  return !!ctx.openID4VCIClientState?.authorizationCodeResponse
118
123
  }
119
124
 
125
+ const oid4vciIsFirstPartyApplication = (ctx: OID4VCIMachineContext, _event: OID4VCIMachineEventTypes): boolean => {
126
+ return !!ctx.serverMetadata?.authorization_challenge_endpoint
127
+ }
128
+
120
129
  const createOID4VCIMachine = (opts?: CreateOID4VCIMachineOpts): OID4VCIStateMachine => {
121
130
  const initialContext: OID4VCIMachineContext = {
122
131
  // 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 +162,8 @@ const createOID4VCIMachine = (opts?: CreateOID4VCIMachineOpts): OID4VCIStateMach
153
162
  | { type: OID4VCIMachineGuards.hasSelectedCredentialsGuard }
154
163
  | { type: OID4VCIMachineGuards.hasAuthorizationResponse }
155
164
  | { type: OID4VCIMachineGuards.isOIDFOriginGuard }
156
- | { type: OID4VCIMachineGuards.contactHasLowTrustGuard },
165
+ | { type: OID4VCIMachineGuards.contactHasLowTrustGuard }
166
+ | { type: OID4VCIMachineGuards.isFirstPartyApplication },
157
167
  services: {} as {
158
168
  [OID4VCIMachineServices.start]: {
159
169
  data: StartResult
@@ -188,6 +198,9 @@ const createOID4VCIMachine = (opts?: CreateOID4VCIMachineOpts): OID4VCIStateMach
188
198
  [OID4VCIMachineServices.getIssuerBranding]: {
189
199
  data: Array<IIssuerLocaleBranding | IBasicIssuerLocaleBranding>
190
200
  }
201
+ [OID4VCIMachineServices.startFirstPartApplicationFlow]: {
202
+ data: void
203
+ }
191
204
  },
192
205
  },
193
206
  context: initialContext,
@@ -332,6 +345,10 @@ const createOID4VCIMachine = (opts?: CreateOID4VCIMachineOpts): OID4VCIStateMach
332
345
  target: OID4VCIMachineStates.selectCredentials,
333
346
  cond: OID4VCIMachineGuards.credentialsToSelectRequiredGuard,
334
347
  },
348
+ {
349
+ target: OID4VCIMachineStates.startFirstPartApplicationFlow,
350
+ cond: OID4VCIMachineGuards.isFirstPartyApplication,
351
+ },
335
352
  {
336
353
  target: OID4VCIMachineStates.initiateAuthorizationRequest,
337
354
  cond: OID4VCIMachineGuards.requireAuthorizationGuard,
@@ -422,6 +439,10 @@ const createOID4VCIMachine = (opts?: CreateOID4VCIMachineOpts): OID4VCIStateMach
422
439
  target: OID4VCIMachineStates.selectCredentials,
423
440
  cond: OID4VCIMachineGuards.credentialsToSelectRequiredGuard,
424
441
  },
442
+ {
443
+ target: OID4VCIMachineStates.startFirstPartApplicationFlow,
444
+ cond: OID4VCIMachineGuards.isFirstPartyApplication,
445
+ },
425
446
  {
426
447
  target: OID4VCIMachineStates.initiateAuthorizationRequest,
427
448
  cond: OID4VCIMachineGuards.requireAuthorizationGuard,
@@ -435,6 +456,41 @@ const createOID4VCIMachine = (opts?: CreateOID4VCIMachineOpts): OID4VCIStateMach
435
456
  },
436
457
  ],
437
458
  },
459
+ [OID4VCIMachineStates.startFirstPartApplicationFlow] :{
460
+ id: OID4VCIMachineStates.startFirstPartApplicationFlow,
461
+ invoke: {
462
+ src: OID4VCIMachineServices.startFirstPartApplicationFlow,
463
+ onDone: [
464
+ {
465
+ target: OID4VCIMachineStates.aborted,
466
+ cond: (_ctx: OID4VCIMachineContext, _event: DoneInvokeEvent<FirstPartyMachineStateTypes>): boolean => _event.data === FirstPartyMachineStateTypes.aborted,
467
+ },
468
+ {
469
+ target: OID4VCIMachineStates.declined,
470
+ cond: (_ctx: OID4VCIMachineContext, _event: DoneInvokeEvent<FirstPartyMachineStateTypes>): boolean => _event.data === FirstPartyMachineStateTypes.declined,
471
+ },
472
+ {
473
+ target: OID4VCIMachineStates.getCredentials,
474
+ actions: assign({
475
+ openID4VCIClientState: (_ctx: OID4VCIMachineContext, _event: DoneInvokeEvent<AuthorizationChallengeCodeResponse>) => {
476
+ const authorizationCodeResponse = toAuthorizationResponsePayload(_event.data)
477
+ return { ..._ctx.openID4VCIClientState!, authorizationCodeResponse }
478
+ }
479
+ })
480
+ }
481
+ ],
482
+ onError: {
483
+ target: OID4VCIMachineStates.handleError,
484
+ actions: assign({
485
+ error: (_ctx: OID4VCIMachineContext, _event: DoneInvokeEvent<ErrorDetails>): ErrorDetails => ({
486
+ title: _event.data.title ?? translate('oid4vci_machine_first_party_error_title'),
487
+ message: _event.data.message,
488
+ stack: _event.data.stack,
489
+ }),
490
+ }),
491
+ },
492
+ },
493
+ },
438
494
  [OID4VCIMachineStates.selectCredentials]: {
439
495
  id: OID4VCIMachineStates.selectCredentials,
440
496
  on: {
@@ -453,6 +509,10 @@ const createOID4VCIMachine = (opts?: CreateOID4VCIMachineOpts): OID4VCIStateMach
453
509
  [OID4VCIMachineStates.transitionFromSelectingCredentials]: {
454
510
  id: OID4VCIMachineStates.transitionFromSelectingCredentials,
455
511
  always: [
512
+ {
513
+ target: OID4VCIMachineStates.startFirstPartApplicationFlow,
514
+ cond: OID4VCIMachineGuards.isFirstPartyApplication,
515
+ },
456
516
  {
457
517
  target: OID4VCIMachineStates.verifyPin,
458
518
  cond: OID4VCIMachineGuards.requirePinGuard,
@@ -726,6 +786,7 @@ export class OID4VCIMachine {
726
786
  oid4vciHasAuthorizationResponse,
727
787
  oid4vciIsOIDFOriginGuard,
728
788
  oid4vciContactHasLowTrustGuard,
789
+ oid4vciIsFirstPartyApplication,
729
790
  ...opts?.guards,
730
791
  },
731
792
  }),
@@ -737,7 +798,7 @@ export class OID4VCIMachine {
737
798
  if (opts?.requireCustomNavigationHook !== true) {
738
799
  if (typeof opts?.stateNavigationListener === 'function') {
739
800
  interpreter.onTransition((snapshot: OID4VCIMachineState): void => {
740
- if (opts?.stateNavigationListener !== undefined) {
801
+ if (opts?.stateNavigationListener) {
741
802
  opts.stateNavigationListener(interpreter, snapshot)
742
803
  }
743
804
  })
@@ -0,0 +1,63 @@
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 && openID4VCIClientState.credentialOffer?.preAuthorizedCode && { issuerState: openID4VCIClientState.credentialOffer?.preAuthorizedCode }),
23
+ ...(!authSession && openID4VCIClientState.credentialOffer?.issuerState && { issuerState: openID4VCIClientState.credentialOffer?.issuerState }),
24
+ ...(presentationDuringIssuanceSession && { presentationDuringIssuanceSession })
25
+ })
26
+ }
27
+
28
+ export const createConfig = async (args: CreateConfigArgs, context: RequiredContext): Promise<CreateConfigResult> => {
29
+ const { presentationUri } = args;
30
+
31
+ if (!presentationUri) {
32
+ return Promise.reject(Error('Missing presentation uri in context'));
33
+ }
34
+
35
+ return context.agent.siopCreateConfig({ url: presentationUri })
36
+ };
37
+
38
+ export const getSiopRequest = async (args: GetSiopRequestArgs, context: RequiredContext): Promise<SiopV2AuthorizationRequestData> => {
39
+ const {didAuthConfig, presentationUri} = args;
40
+
41
+ if (presentationUri === undefined) {
42
+ return Promise.reject(Error('Missing presentation uri in context'));
43
+ }
44
+
45
+ if (didAuthConfig === undefined) {
46
+ return Promise.reject(Error('Missing did auth config in context'));
47
+ }
48
+
49
+ return context.agent.siopGetSiopRequest({ didAuthConfig, url: presentationUri })
50
+ }
51
+
52
+ export const sendAuthorizationResponse = async (args: SendAuthorizationResponseArgs, context: RequiredContext): Promise<string> => {
53
+ const { didAuthConfig, authorizationRequestData, selectedCredentials } = args
54
+
55
+ const responseData = await context.agent.siopSendResponse({
56
+ authorizationRequestData,
57
+ selectedCredentials,
58
+ didAuthConfig,
59
+ isFirstParty: true
60
+ })
61
+
62
+ return (<AuthorizationChallengeValidationResponse>responseData.body).presentation_during_issuance_session
63
+ }
@@ -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'
@@ -56,12 +57,16 @@ import {
56
57
  SelectAppLocaleBrandingArgs,
57
58
  VerificationResult,
58
59
  VerifyCredentialToAcceptArgs,
60
+ StartFirstPartApplicationMachine,
61
+ RequiredContext
59
62
  } from '../types/IOID4VCIHolder'
60
63
  import {
61
64
  oid4vciGetCredentialBrandingFrom,
62
65
  sdJwtGetCredentialBrandingFrom,
63
66
  issuerLocaleBrandingFrom
64
- } from './OIDC4VCIBrandingMapper'
67
+ } from '../mappers/OIDC4VCIBrandingMapper'
68
+ import { FirstPartyMachine } from '../machines/firstPartyMachine'
69
+ import { FirstPartyMachineState, FirstPartyMachineStateTypes } from '../types/FirstPartyMachine'
65
70
 
66
71
  export const getCredentialBranding = async (args: GetCredentialBrandingArgs): Promise<Record<string, Array<IBasicCredentialLocaleBranding>>> => {
67
72
  const { credentialsSupported, context } = args
@@ -615,3 +620,45 @@ export const getIssuanceCryptoSuite = async (opts: GetIssuanceCryptoSuiteArgs):
615
620
  return Promise.reject(Error(`Credential format '${credentialSupported.format}' not supported`))
616
621
  }
617
622
  }
623
+
624
+ export const startFirstPartApplicationMachine = async (args: StartFirstPartApplicationMachine, context: RequiredContext): 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
+ };