@oneblink/apps-react 10.3.1 → 11.0.0-beta.10

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 (69) hide show
  1. package/dist/apps/auth-service.d.ts +15 -7
  2. package/dist/apps/auth-service.js +18 -8
  3. package/dist/apps/auth-service.js.map +1 -1
  4. package/dist/apps/index.d.ts +10 -0
  5. package/dist/apps/index.js +10 -0
  6. package/dist/apps/index.js.map +1 -1
  7. package/dist/apps/mfa-service.d.ts +29 -0
  8. package/dist/apps/mfa-service.js +30 -0
  9. package/dist/apps/mfa-service.js.map +1 -0
  10. package/dist/apps/services/AWSCognitoClient.d.ts +43 -5
  11. package/dist/apps/services/AWSCognitoClient.js +249 -26
  12. package/dist/apps/services/AWSCognitoClient.js.map +1 -1
  13. package/dist/apps/services/cognito.d.ts +42 -52
  14. package/dist/apps/services/cognito.js +68 -60
  15. package/dist/apps/services/cognito.js.map +1 -1
  16. package/dist/components/mfa/MfaAuthenticatorAppDialog.d.ts +12 -0
  17. package/dist/components/mfa/MfaAuthenticatorAppDialog.js +64 -0
  18. package/dist/components/mfa/MfaAuthenticatorAppDialog.js.map +1 -0
  19. package/dist/components/mfa/MfaDisableDialog.d.ts +10 -0
  20. package/dist/components/mfa/MfaDisableDialog.js +31 -0
  21. package/dist/components/mfa/MfaDisableDialog.js.map +1 -0
  22. package/dist/components/mfa/MfaErrorSnackbar.d.ts +10 -0
  23. package/dist/components/mfa/MfaErrorSnackbar.js +17 -0
  24. package/dist/components/mfa/MfaErrorSnackbar.js.map +1 -0
  25. package/dist/components/mfa/MfaMethodRow.d.ts +20 -0
  26. package/dist/components/mfa/MfaMethodRow.js +10 -0
  27. package/dist/components/mfa/MfaMethodRow.js.map +1 -0
  28. package/dist/components/mfa/MfaPhoneNumberDialog.d.ts +11 -0
  29. package/dist/components/mfa/MfaPhoneNumberDialog.js +95 -0
  30. package/dist/components/mfa/MfaPhoneNumberDialog.js.map +1 -0
  31. package/dist/components/mfa/MfaRemovePhoneNumberDialog.d.ts +10 -0
  32. package/dist/components/mfa/MfaRemovePhoneNumberDialog.js +24 -0
  33. package/dist/components/mfa/MfaRemovePhoneNumberDialog.js.map +1 -0
  34. package/dist/components/mfa/MfaStatusChip.d.ts +10 -0
  35. package/dist/components/mfa/MfaStatusChip.js +29 -0
  36. package/dist/components/mfa/MfaStatusChip.js.map +1 -0
  37. package/dist/components/mfa/MfaSuccessSnackbar.d.ts +10 -0
  38. package/dist/components/mfa/MfaSuccessSnackbar.js +17 -0
  39. package/dist/components/mfa/MfaSuccessSnackbar.js.map +1 -0
  40. package/dist/components/mfa/MultiFactorAuthentication.d.ts +10 -10
  41. package/dist/components/mfa/MultiFactorAuthentication.js +46 -40
  42. package/dist/components/mfa/MultiFactorAuthentication.js.map +1 -1
  43. package/dist/form-elements/FormElementBarcodeScanner.js +66 -18
  44. package/dist/form-elements/FormElementBarcodeScanner.js.map +1 -1
  45. package/dist/hooks/useLogin.d.ts +28 -8
  46. package/dist/hooks/useLogin.js +67 -7
  47. package/dist/hooks/useLogin.js.map +1 -1
  48. package/dist/hooks/useMfa.d.ts +98 -31
  49. package/dist/hooks/useMfa.js +436 -68
  50. package/dist/hooks/useMfa.js.map +1 -1
  51. package/dist/hooks/useResendCoolDown.d.ts +5 -0
  52. package/dist/hooks/useResendCoolDown.js +27 -0
  53. package/dist/hooks/useResendCoolDown.js.map +1 -0
  54. package/dist/index.d.ts +9 -0
  55. package/dist/index.js +9 -0
  56. package/dist/index.js.map +1 -1
  57. package/dist/styles/barcode-scanner.scss +4 -0
  58. package/dist/styles.css +5 -0
  59. package/dist/styles.scss +1 -0
  60. package/dist/utils/joinArray.d.ts +1 -0
  61. package/dist/utils/joinArray.js +7 -0
  62. package/dist/utils/joinArray.js.map +1 -0
  63. package/dist/utils/mfa-requirement.d.ts +12 -0
  64. package/dist/utils/mfa-requirement.js +100 -0
  65. package/dist/utils/mfa-requirement.js.map +1 -0
  66. package/package.json +4 -4
  67. package/dist/components/mfa/MfaDialog.d.ts +0 -9
  68. package/dist/components/mfa/MfaDialog.js +0 -47
  69. package/dist/components/mfa/MfaDialog.js.map +0 -1
@@ -1,21 +1,82 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from 'react';
3
- import { authService } from '../apps';
3
+ import { mfaService } from '../apps';
4
+ import useLoadDataEffect from './useLoadDataEffect';
4
5
  export const MfaContext = React.createContext({
5
6
  isExternalIdentityProviderUser: false,
6
7
  isLoading: true,
7
8
  isMfaEnabled: false,
9
+ mfaSettings: mfaService.DEFAULT_MFA_SETTINGS,
10
+ isSetupSuccessOpen: false,
8
11
  isSettingUpMfa: false,
9
- isDisablingMfa: false,
10
- beginMfaSetup: () => { },
11
- cancelMfaSetup: () => { },
12
- completeMfaSetup: () => { },
13
- beginDisablingMfa: () => { },
12
+ isSetupMethodDialogOpen: false,
13
+ isSettingPreferredMfaMethod: false,
14
+ isPhoneNumberDialogOpen: false,
15
+ isRemovePhoneNumberDialogOpen: false,
16
+ beginMfaSetup: async () => { },
17
+ openMfaSetupMethodDialog: () => { },
18
+ closeMfaSetupMethodDialog: () => { },
19
+ beginDisablingMfaMethod: () => { },
20
+ setPreferredMfaMethod: async () => { },
21
+ openPhoneNumberDialog: () => { },
22
+ closePhoneNumberDialog: () => { },
23
+ savePhoneNumber: async () => { },
24
+ verifyPhoneNumber: async () => { },
25
+ beginRemovingPhoneNumber: () => { },
26
+ cancelRemovingPhoneNumber: () => { },
27
+ completeRemovingPhoneNumber: async () => { },
28
+ hideSetupSuccess: () => { },
29
+ cancelMfaAuthenticatorAppSetup: () => { },
30
+ completeMfaAuthenticatorAppSetup: async () => { },
14
31
  cancelDisablingMfa: () => { },
15
- completeDisablingMfa: () => { },
32
+ completeDisablingMfa: async () => { },
16
33
  clearMfaSetupError: () => { },
17
34
  loadMfa: () => { },
18
35
  });
36
+ function getIsMfaEnabled(mfaSettings) {
37
+ return mfaSettings.authenticator.enabled || mfaSettings.sms.enabled;
38
+ }
39
+ function hasPreferredMfaMethod(mfaSettings) {
40
+ return ((mfaSettings.authenticator.enabled &&
41
+ mfaSettings.authenticator.preferred) ||
42
+ (mfaSettings.sms.enabled && mfaSettings.sms.preferred));
43
+ }
44
+ function enableSmsMfaInSettings(mfaSettings, smsPreferred) {
45
+ return {
46
+ ...mfaSettings,
47
+ sms: {
48
+ ...mfaSettings.sms,
49
+ enabled: true,
50
+ preferred: smsPreferred,
51
+ },
52
+ };
53
+ }
54
+ function enableAuthenticatorMfaInSettings(mfaSettings, authenticatorPreferred) {
55
+ return {
56
+ ...mfaSettings,
57
+ authenticator: {
58
+ enabled: true,
59
+ preferred: authenticatorPreferred,
60
+ },
61
+ sms: {
62
+ ...mfaSettings.sms,
63
+ preferred: authenticatorPreferred ? false : mfaSettings.sms.preferred,
64
+ },
65
+ };
66
+ }
67
+ function setPreferredMfaMethodInSettings(mfaSettings, mfaMethod) {
68
+ return {
69
+ ...mfaSettings,
70
+ authenticator: {
71
+ ...mfaSettings.authenticator,
72
+ preferred: mfaMethod === 'authenticator',
73
+ },
74
+ sms: {
75
+ ...mfaSettings.sms,
76
+ preferred: mfaMethod === 'sms',
77
+ },
78
+ };
79
+ }
19
80
  /**
20
81
  * React Component that provides the context for the
21
82
  * `useUserMeetsMfaRequirement()` hook and `<MultiFactorAuthentication />`
@@ -32,23 +93,36 @@ export const MfaContext = React.createContext({
32
93
  * useUserMeetsMfaRequirement,
33
94
  * } from '@oneblink/apps-react'
34
95
  *
35
- * function Component() {
36
- * const { isLoading, userMeetsMfaRequirement } =
37
- * useUserMeetsMfaRequirement(true)
38
- * // use MFA Requirement details here
96
+ * function Component({ teamMemberMfaRequirement }) {
97
+ * const { mfaSetupRequired, isLoading, loadingError, refreshMfa } =
98
+ * useUserMeetsMfaRequirement(teamMemberMfaRequirement)
99
+ *
100
+ * if (isLoading) {
101
+ * return <Loading />
102
+ * }
103
+ *
104
+ * if (loadingError) {
105
+ * return <Error onRetry={refreshMfa} />
106
+ * }
107
+ *
108
+ * if (mfaSetupRequired) {
109
+ * return <ConfigureMfa />
110
+ * }
111
+ *
112
+ * return <Application />
39
113
  * }
40
114
  *
41
- * function App() {
115
+ * function App({ teamMemberMfaRequirement }) {
42
116
  * return (
43
117
  * <MfaProvider isExternalIdentityProviderUser={false}>
44
- * <Component />
118
+ * <Component teamMemberMfaRequirement={teamMemberMfaRequirement} />
45
119
  * </MfaProvider>
46
120
  * )
47
121
  * }
48
122
  *
49
123
  * const root = document.getElementById('root')
50
124
  * if (root) {
51
- * ReactDOM.render(<App />, root)
125
+ * ReactDOM.render(<App teamMemberMfaRequirement={mfaRequirement} />, root)
52
126
  * }
53
127
  * ```
54
128
  *
@@ -61,112 +135,366 @@ export function MfaProvider({ children, isExternalIdentityProviderUser, }) {
61
135
  isExternalIdentityProviderUser,
62
136
  isLoading: !isExternalIdentityProviderUser,
63
137
  isMfaEnabled: false,
138
+ mfaSettings: mfaService.DEFAULT_MFA_SETTINGS,
139
+ isSetupSuccessOpen: false,
64
140
  isSettingUpMfa: false,
65
- isDisablingMfa: false,
141
+ isSetupMethodDialogOpen: false,
142
+ isSettingPreferredMfaMethod: false,
143
+ isPhoneNumberDialogOpen: false,
144
+ isRemovePhoneNumberDialogOpen: false,
66
145
  });
67
- const loadMfa = React.useCallback(async (abortSignal) => {
146
+ const handleLoadMfa = React.useCallback(async (abortSignal) => {
147
+ if (isExternalIdentityProviderUser) {
148
+ return;
149
+ }
68
150
  setState((currentState) => ({
69
151
  ...currentState,
70
152
  isLoading: true,
71
153
  isMfaEnabled: false,
154
+ mfaSettings: mfaService.DEFAULT_MFA_SETTINGS,
72
155
  loadingError: undefined,
73
156
  }));
74
157
  try {
75
- const newIsMfaEnabled = await authService.checkIsMfaEnabled();
76
- if (!(abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.aborted)) {
158
+ const mfaSettings = await mfaService.getMfaSettings(abortSignal);
159
+ if (!abortSignal.aborted) {
77
160
  setState((currentState) => ({
78
161
  ...currentState,
79
162
  isLoading: false,
80
- isMfaEnabled: newIsMfaEnabled,
163
+ mfaSettings,
164
+ isMfaEnabled: getIsMfaEnabled(mfaSettings),
81
165
  }));
82
166
  }
83
167
  }
84
168
  catch (error) {
169
+ if (abortSignal.aborted) {
170
+ return;
171
+ }
85
172
  setState((currentState) => ({
86
173
  ...currentState,
87
174
  isLoading: false,
88
175
  loadingError: error,
89
176
  }));
90
177
  }
91
- }, []);
178
+ }, [isExternalIdentityProviderUser]);
179
+ const loadMfa = useLoadDataEffect(handleLoadMfa);
92
180
  const clearMfaSetupError = React.useCallback(() => {
93
181
  setState((currentState) => ({
94
182
  ...currentState,
95
183
  setupError: undefined,
96
184
  }));
97
185
  }, []);
98
- const cancelMfaSetup = React.useCallback(() => {
186
+ const openMfaSetupMethodDialog = React.useCallback(() => {
187
+ setState((currentState) => ({
188
+ ...currentState,
189
+ isSetupMethodDialogOpen: true,
190
+ }));
191
+ }, []);
192
+ const closeMfaSetupMethodDialog = React.useCallback(() => {
193
+ setState((currentState) => ({
194
+ ...currentState,
195
+ isSetupMethodDialogOpen: false,
196
+ }));
197
+ }, []);
198
+ const hideSetupSuccess = React.useCallback(() => {
199
+ setState((currentState) => ({
200
+ ...currentState,
201
+ isSetupSuccessOpen: false,
202
+ }));
203
+ }, []);
204
+ const cancelMfaAuthenticatorAppSetup = React.useCallback(() => {
205
+ setState((currentState) => ({
206
+ ...currentState,
207
+ mfaAuthenticatorAppSetup: undefined,
208
+ settingUpMfaMethod: undefined,
209
+ }));
210
+ }, []);
211
+ const completeMfaAuthenticatorAppSetup = React.useCallback(async () => {
212
+ setState((currentState) => {
213
+ const authenticatorPreferred = !hasPreferredMfaMethod(currentState.mfaSettings);
214
+ const mfaSettings = enableAuthenticatorMfaInSettings(currentState.mfaSettings, authenticatorPreferred);
215
+ return {
216
+ ...currentState,
217
+ isSetupSuccessOpen: true,
218
+ isMfaEnabled: getIsMfaEnabled(mfaSettings),
219
+ mfaSettings,
220
+ mfaAuthenticatorAppSetup: undefined,
221
+ settingUpMfaMethod: undefined,
222
+ };
223
+ });
224
+ }, []);
225
+ const openPhoneNumberDialog = React.useCallback(() => {
226
+ setState((currentState) => ({
227
+ ...currentState,
228
+ isPhoneNumberDialogOpen: true,
229
+ phoneVerificationCodeSentAt: undefined,
230
+ setupError: undefined,
231
+ }));
232
+ }, []);
233
+ const closePhoneNumberDialog = React.useCallback(() => {
234
+ setState((currentState) => ({
235
+ ...currentState,
236
+ isPhoneNumberDialogOpen: false,
237
+ phoneVerificationCodeSentAt: undefined,
238
+ }));
239
+ }, []);
240
+ const setupSmsMfaMethod = React.useCallback(async () => {
241
+ let smsPreferred = false;
242
+ setState((currentState) => {
243
+ smsPreferred = !hasPreferredMfaMethod(currentState.mfaSettings);
244
+ return currentState;
245
+ });
246
+ await mfaService.setupSmsMfa({
247
+ preferred: smsPreferred,
248
+ });
249
+ setState((currentState) => {
250
+ const mfaSettings = enableSmsMfaInSettings(currentState.mfaSettings, smsPreferred);
251
+ return {
252
+ ...currentState,
253
+ isSetupSuccessOpen: true,
254
+ isSettingUpMfa: false,
255
+ settingUpMfaMethod: undefined,
256
+ isMfaEnabled: getIsMfaEnabled(mfaSettings),
257
+ mfaSettings,
258
+ };
259
+ });
260
+ }, []);
261
+ const savePhoneNumber = React.useCallback(async (phoneNumber) => {
262
+ setState((currentState) => ({
263
+ ...currentState,
264
+ setupError: undefined,
265
+ }));
266
+ try {
267
+ const { isPhoneNumberVerified } = await mfaService.updateUserPhoneNumber(phoneNumber);
268
+ if (!isPhoneNumberVerified) {
269
+ setState((currentState) => ({
270
+ ...currentState,
271
+ phoneVerificationCodeSentAt: Date.now(),
272
+ mfaSettings: {
273
+ ...currentState.mfaSettings,
274
+ sms: {
275
+ ...currentState.mfaSettings.sms,
276
+ phoneNumber,
277
+ isPhoneNumberVerified: false,
278
+ },
279
+ },
280
+ }));
281
+ return;
282
+ }
283
+ setState((currentState) => ({
284
+ ...currentState,
285
+ mfaSettings: {
286
+ ...currentState.mfaSettings,
287
+ sms: {
288
+ ...currentState.mfaSettings.sms,
289
+ phoneNumber,
290
+ isPhoneNumberVerified,
291
+ },
292
+ },
293
+ isPhoneNumberDialogOpen: false,
294
+ phoneVerificationCodeSentAt: undefined,
295
+ }));
296
+ await setupSmsMfaMethod();
297
+ }
298
+ catch (error) {
299
+ setState((currentState) => ({
300
+ ...currentState,
301
+ setupError: error,
302
+ }));
303
+ }
304
+ }, [setupSmsMfaMethod]);
305
+ const verifyPhoneNumber = React.useCallback(async (code) => {
306
+ setState((currentState) => ({
307
+ ...currentState,
308
+ setupError: undefined,
309
+ }));
310
+ try {
311
+ await mfaService.verifyUserPhoneNumber(code);
312
+ setState((currentState) => ({
313
+ ...currentState,
314
+ mfaSettings: {
315
+ ...currentState.mfaSettings,
316
+ sms: {
317
+ ...currentState.mfaSettings.sms,
318
+ isPhoneNumberVerified: true,
319
+ },
320
+ },
321
+ isPhoneNumberDialogOpen: false,
322
+ phoneVerificationCodeSentAt: undefined,
323
+ }));
324
+ await setupSmsMfaMethod();
325
+ }
326
+ catch (error) {
327
+ setState((currentState) => ({
328
+ ...currentState,
329
+ setupError: error,
330
+ }));
331
+ }
332
+ }, [setupSmsMfaMethod]);
333
+ const beginRemovingPhoneNumber = React.useCallback(() => {
334
+ setState((currentState) => ({
335
+ ...currentState,
336
+ isRemovePhoneNumberDialogOpen: true,
337
+ setupError: undefined,
338
+ }));
339
+ }, []);
340
+ const cancelRemovingPhoneNumber = React.useCallback(() => {
99
341
  setState((currentState) => ({
100
342
  ...currentState,
101
- mfaSetup: undefined,
343
+ isRemovePhoneNumberDialogOpen: false,
102
344
  }));
103
345
  }, []);
104
- const completeMfaSetup = React.useCallback(() => {
346
+ const completeRemovingPhoneNumber = React.useCallback(async () => {
105
347
  setState((currentState) => ({
106
348
  ...currentState,
107
- isMfaEnabled: true,
108
- mfaSetup: undefined,
349
+ setupError: undefined,
109
350
  }));
351
+ try {
352
+ await mfaService.removeUserPhoneNumber();
353
+ setState((currentState) => ({
354
+ ...currentState,
355
+ isRemovePhoneNumberDialogOpen: false,
356
+ mfaSettings: {
357
+ ...currentState.mfaSettings,
358
+ sms: {
359
+ ...currentState.mfaSettings.sms,
360
+ phoneNumber: undefined,
361
+ isPhoneNumberVerified: false,
362
+ },
363
+ },
364
+ }));
365
+ }
366
+ catch (error) {
367
+ setState((currentState) => ({
368
+ ...currentState,
369
+ setupError: error,
370
+ }));
371
+ }
110
372
  }, []);
111
- const beginMfaSetup = React.useCallback(async () => {
373
+ const beginMfaSetup = React.useCallback(async (mfaMethod) => {
112
374
  setState((currentState) => ({
113
375
  ...currentState,
114
376
  isSettingUpMfa: true,
115
- mfaSetup: undefined,
377
+ settingUpMfaMethod: mfaMethod,
378
+ mfaAuthenticatorAppSetup: undefined,
116
379
  setupError: undefined,
117
380
  }));
118
381
  try {
119
- const newMfaSetup = await authService.setupMfa();
382
+ let mfaSettings = mfaService.DEFAULT_MFA_SETTINGS;
383
+ setState((currentState) => {
384
+ mfaSettings = currentState.mfaSettings;
385
+ return currentState;
386
+ });
387
+ if (mfaMethod === 'sms') {
388
+ if (!mfaSettings.sms.phoneNumber ||
389
+ !mfaSettings.sms.isPhoneNumberVerified) {
390
+ setState((currentState) => ({
391
+ ...currentState,
392
+ isSettingUpMfa: false,
393
+ settingUpMfaMethod: undefined,
394
+ isSetupMethodDialogOpen: false,
395
+ isPhoneNumberDialogOpen: true,
396
+ }));
397
+ return;
398
+ }
399
+ await setupSmsMfaMethod();
400
+ setState((currentState) => ({
401
+ ...currentState,
402
+ isSetupMethodDialogOpen: false,
403
+ }));
404
+ return;
405
+ }
406
+ const hasPreferredMethod = hasPreferredMfaMethod(mfaSettings);
407
+ const newMfaAuthenticatorAppSetup = await mfaService.setupMfaAuthenticatorApp({
408
+ preferred: !hasPreferredMethod,
409
+ });
120
410
  setState((currentState) => ({
121
411
  ...currentState,
122
412
  isSettingUpMfa: false,
123
- mfaSetup: newMfaSetup,
413
+ isSetupMethodDialogOpen: false,
414
+ mfaAuthenticatorAppSetup: newMfaAuthenticatorAppSetup,
124
415
  }));
125
416
  }
126
417
  catch (error) {
127
418
  setState((currentState) => ({
128
419
  ...currentState,
129
420
  isSettingUpMfa: false,
421
+ settingUpMfaMethod: undefined,
130
422
  setupError: error,
131
423
  }));
132
424
  }
133
- }, []);
134
- const beginDisablingMfa = React.useCallback(() => {
425
+ }, [setupSmsMfaMethod]);
426
+ const disablingMfaMethodRef = React.useRef(undefined);
427
+ const beginDisablingMfaMethod = React.useCallback((mfaMethod) => {
428
+ disablingMfaMethodRef.current = mfaMethod;
135
429
  setState((currentState) => ({
136
430
  ...currentState,
137
- isDisablingMfa: true,
431
+ disablingMfaMethod: mfaMethod,
138
432
  }));
139
433
  }, []);
140
434
  const cancelDisablingMfa = React.useCallback(() => {
435
+ disablingMfaMethodRef.current = undefined;
141
436
  setState((currentState) => ({
142
437
  ...currentState,
143
- isDisablingMfa: false,
438
+ disablingMfaMethod: undefined,
144
439
  }));
145
440
  }, []);
146
441
  const completeDisablingMfa = React.useCallback(async () => {
147
- await authService.disableMfa();
442
+ const disablingMfaMethod = disablingMfaMethodRef.current;
443
+ if (!disablingMfaMethod) {
444
+ return;
445
+ }
446
+ await mfaService.disableMfaMethod(disablingMfaMethod);
447
+ const mfaSettings = await mfaService.getMfaSettings();
448
+ disablingMfaMethodRef.current = undefined;
148
449
  setState((currentState) => ({
149
450
  ...currentState,
150
- isDisablingMfa: false,
151
- isMfaEnabled: false,
451
+ disablingMfaMethod: undefined,
452
+ mfaSettings,
453
+ isMfaEnabled: getIsMfaEnabled(mfaSettings),
152
454
  }));
153
455
  }, []);
154
- React.useEffect(() => {
155
- if (isExternalIdentityProviderUser) {
156
- return;
456
+ const setPreferredMfaMethod = React.useCallback(async (mfaMethod) => {
457
+ setState((currentState) => ({
458
+ ...currentState,
459
+ isSettingPreferredMfaMethod: true,
460
+ setupError: undefined,
461
+ }));
462
+ try {
463
+ await mfaService.setPreferredMfaMethod(mfaMethod);
464
+ setState((currentState) => ({
465
+ ...currentState,
466
+ isSettingPreferredMfaMethod: false,
467
+ mfaSettings: setPreferredMfaMethodInSettings(currentState.mfaSettings, mfaMethod),
468
+ }));
157
469
  }
158
- loadMfa();
159
- return () => { };
160
- }, [isExternalIdentityProviderUser, loadMfa]);
470
+ catch (error) {
471
+ setState((currentState) => ({
472
+ ...currentState,
473
+ isSettingPreferredMfaMethod: false,
474
+ setupError: error,
475
+ }));
476
+ }
477
+ }, []);
161
478
  const value = React.useMemo(() => {
162
479
  return {
163
480
  ...state,
164
481
  clearMfaSetupError,
165
482
  loadMfa,
166
483
  beginMfaSetup,
167
- cancelMfaSetup,
168
- completeMfaSetup,
169
- beginDisablingMfa,
484
+ openMfaSetupMethodDialog,
485
+ closeMfaSetupMethodDialog,
486
+ beginDisablingMfaMethod,
487
+ setPreferredMfaMethod,
488
+ openPhoneNumberDialog,
489
+ closePhoneNumberDialog,
490
+ savePhoneNumber,
491
+ verifyPhoneNumber,
492
+ beginRemovingPhoneNumber,
493
+ cancelRemovingPhoneNumber,
494
+ completeRemovingPhoneNumber,
495
+ hideSetupSuccess,
496
+ cancelMfaAuthenticatorAppSetup,
497
+ completeMfaAuthenticatorAppSetup,
170
498
  cancelDisablingMfa,
171
499
  completeDisablingMfa,
172
500
  };
@@ -175,9 +503,20 @@ export function MfaProvider({ children, isExternalIdentityProviderUser, }) {
175
503
  clearMfaSetupError,
176
504
  loadMfa,
177
505
  beginMfaSetup,
178
- cancelMfaSetup,
179
- completeMfaSetup,
180
- beginDisablingMfa,
506
+ openMfaSetupMethodDialog,
507
+ closeMfaSetupMethodDialog,
508
+ beginDisablingMfaMethod,
509
+ setPreferredMfaMethod,
510
+ openPhoneNumberDialog,
511
+ closePhoneNumberDialog,
512
+ savePhoneNumber,
513
+ verifyPhoneNumber,
514
+ beginRemovingPhoneNumber,
515
+ cancelRemovingPhoneNumber,
516
+ completeRemovingPhoneNumber,
517
+ hideSetupSuccess,
518
+ cancelMfaAuthenticatorAppSetup,
519
+ completeMfaAuthenticatorAppSetup,
181
520
  cancelDisablingMfa,
182
521
  completeDisablingMfa,
183
522
  ]);
@@ -187,35 +526,64 @@ export default function useMfa() {
187
526
  return React.useContext(MfaContext);
188
527
  }
189
528
  /**
190
- * React hook to check if the logged in user meets the MFA requirement of your
191
- * application. Will throw an Error if used outside of the `<MfaProvider />`
192
- * component.
529
+ * React hook to determine whether the logged in user must set up MFA before
530
+ * accessing your application. Reads MFA settings from the `<MfaProvider />`
531
+ * context. Will throw an Error if used outside of `<MfaProvider />`.
193
532
  *
194
- * Example
533
+ * Users signed in via an external identity provider are not required to set up
534
+ * MFA.
535
+ *
536
+ * #### Example
195
537
  *
196
538
  * ```js
197
- * import { useUserMeetsMfaRequirement } from '@oneblink/apps-react'
539
+ * import { MfaProvider, useUserMeetsMfaRequirement } from '@oneblink/apps-react'
540
+ *
541
+ * function Component({ teamMemberMfaRequirement }) {
542
+ * const { mfaSetupRequired, isLoading, loadingError, refreshMfa } =
543
+ * useUserMeetsMfaRequirement(teamMemberMfaRequirement)
544
+ *
545
+ * if (isLoading) {
546
+ * return <Loading />
547
+ * }
198
548
  *
199
- * const isMfaRequired = true
549
+ * if (loadingError) {
550
+ * return <Error onRetry={refreshMfa} />
551
+ * }
200
552
  *
201
- * function Component() {
202
- * const userMeetsMfaRequirement =
203
- * useUserMeetsMfaRequirement(isMfaRequired)
553
+ * if (mfaSetupRequired) {
554
+ * return <ConfigureMfa />
555
+ * }
556
+ *
557
+ * return <Application />
204
558
  * }
205
559
  * ```
206
560
  *
207
- * @returns
561
+ * @param mfaRequirement - The MFA requirement to enforce, e.g. from your
562
+ * organisation or app settings.
563
+ * @returns Whether MFA setup is required, along with loading state from the MFA
564
+ * provider.
208
565
  * @group Hooks
209
566
  */
210
- export function useUserMeetsMfaRequirement(isMfaRequired) {
211
- const context = React.useContext(MfaContext);
212
- if (!context) {
213
- throw new Error(`"useUserMeetsMfaRequirement" hook was used outside of the "<MfaProvider />" component's children.`);
214
- }
215
- const { isMfaEnabled, isExternalIdentityProviderUser } = context;
216
- if (!isMfaRequired || isExternalIdentityProviderUser) {
217
- return true;
218
- }
219
- return isMfaEnabled;
567
+ export function useUserMeetsMfaRequirement(mfaRequirement) {
568
+ const { isLoading, loadingError, mfaSettings, loadMfa, isExternalIdentityProviderUser, } = useMfa();
569
+ return React.useMemo(() => {
570
+ const mfaRequired = mfaService.isMfaRequired(mfaRequirement);
571
+ const shouldCheckMfa = mfaRequired && !isExternalIdentityProviderUser;
572
+ const meetsRequirement = mfaService.userMeetsMfaRequirement(mfaRequirement, mfaSettings);
573
+ const mfaSetupRequired = shouldCheckMfa && !isLoading && !loadingError && !meetsRequirement;
574
+ return {
575
+ mfaSetupRequired,
576
+ isLoading: shouldCheckMfa && isLoading,
577
+ loadingError: shouldCheckMfa ? loadingError : undefined,
578
+ refreshMfa: loadMfa,
579
+ };
580
+ }, [
581
+ mfaRequirement,
582
+ isExternalIdentityProviderUser,
583
+ mfaSettings,
584
+ isLoading,
585
+ loadingError,
586
+ loadMfa,
587
+ ]);
220
588
  }
221
589
  //# sourceMappingURL=useMfa.js.map