@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.
- package/dist/apps/auth-service.d.ts +15 -7
- package/dist/apps/auth-service.js +18 -8
- package/dist/apps/auth-service.js.map +1 -1
- package/dist/apps/index.d.ts +10 -0
- package/dist/apps/index.js +10 -0
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/mfa-service.d.ts +29 -0
- package/dist/apps/mfa-service.js +30 -0
- package/dist/apps/mfa-service.js.map +1 -0
- package/dist/apps/services/AWSCognitoClient.d.ts +43 -5
- package/dist/apps/services/AWSCognitoClient.js +249 -26
- package/dist/apps/services/AWSCognitoClient.js.map +1 -1
- package/dist/apps/services/cognito.d.ts +42 -52
- package/dist/apps/services/cognito.js +68 -60
- package/dist/apps/services/cognito.js.map +1 -1
- package/dist/components/mfa/MfaAuthenticatorAppDialog.d.ts +12 -0
- package/dist/components/mfa/MfaAuthenticatorAppDialog.js +64 -0
- package/dist/components/mfa/MfaAuthenticatorAppDialog.js.map +1 -0
- package/dist/components/mfa/MfaDisableDialog.d.ts +10 -0
- package/dist/components/mfa/MfaDisableDialog.js +31 -0
- package/dist/components/mfa/MfaDisableDialog.js.map +1 -0
- package/dist/components/mfa/MfaErrorSnackbar.d.ts +10 -0
- package/dist/components/mfa/MfaErrorSnackbar.js +17 -0
- package/dist/components/mfa/MfaErrorSnackbar.js.map +1 -0
- package/dist/components/mfa/MfaMethodRow.d.ts +20 -0
- package/dist/components/mfa/MfaMethodRow.js +10 -0
- package/dist/components/mfa/MfaMethodRow.js.map +1 -0
- package/dist/components/mfa/MfaPhoneNumberDialog.d.ts +11 -0
- package/dist/components/mfa/MfaPhoneNumberDialog.js +95 -0
- package/dist/components/mfa/MfaPhoneNumberDialog.js.map +1 -0
- package/dist/components/mfa/MfaRemovePhoneNumberDialog.d.ts +10 -0
- package/dist/components/mfa/MfaRemovePhoneNumberDialog.js +24 -0
- package/dist/components/mfa/MfaRemovePhoneNumberDialog.js.map +1 -0
- package/dist/components/mfa/MfaStatusChip.d.ts +10 -0
- package/dist/components/mfa/MfaStatusChip.js +29 -0
- package/dist/components/mfa/MfaStatusChip.js.map +1 -0
- package/dist/components/mfa/MfaSuccessSnackbar.d.ts +10 -0
- package/dist/components/mfa/MfaSuccessSnackbar.js +17 -0
- package/dist/components/mfa/MfaSuccessSnackbar.js.map +1 -0
- package/dist/components/mfa/MultiFactorAuthentication.d.ts +10 -10
- package/dist/components/mfa/MultiFactorAuthentication.js +46 -40
- package/dist/components/mfa/MultiFactorAuthentication.js.map +1 -1
- package/dist/form-elements/FormElementBarcodeScanner.js +66 -18
- package/dist/form-elements/FormElementBarcodeScanner.js.map +1 -1
- package/dist/hooks/useLogin.d.ts +28 -8
- package/dist/hooks/useLogin.js +67 -7
- package/dist/hooks/useLogin.js.map +1 -1
- package/dist/hooks/useMfa.d.ts +98 -31
- package/dist/hooks/useMfa.js +436 -68
- package/dist/hooks/useMfa.js.map +1 -1
- package/dist/hooks/useResendCoolDown.d.ts +5 -0
- package/dist/hooks/useResendCoolDown.js +27 -0
- package/dist/hooks/useResendCoolDown.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/styles/barcode-scanner.scss +4 -0
- package/dist/styles.css +5 -0
- package/dist/styles.scss +1 -0
- package/dist/utils/joinArray.d.ts +1 -0
- package/dist/utils/joinArray.js +7 -0
- package/dist/utils/joinArray.js.map +1 -0
- package/dist/utils/mfa-requirement.d.ts +12 -0
- package/dist/utils/mfa-requirement.js +100 -0
- package/dist/utils/mfa-requirement.js.map +1 -0
- package/package.json +4 -4
- package/dist/components/mfa/MfaDialog.d.ts +0 -9
- package/dist/components/mfa/MfaDialog.js +0 -47
- package/dist/components/mfa/MfaDialog.js.map +0 -1
package/dist/hooks/useMfa.js
CHANGED
|
@@ -1,21 +1,82 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import {
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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,
|
|
37
|
-
* useUserMeetsMfaRequirement(
|
|
38
|
-
*
|
|
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
|
-
|
|
141
|
+
isSetupMethodDialogOpen: false,
|
|
142
|
+
isSettingPreferredMfaMethod: false,
|
|
143
|
+
isPhoneNumberDialogOpen: false,
|
|
144
|
+
isRemovePhoneNumberDialogOpen: false,
|
|
66
145
|
});
|
|
67
|
-
const
|
|
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
|
|
76
|
-
if (!
|
|
158
|
+
const mfaSettings = await mfaService.getMfaSettings(abortSignal);
|
|
159
|
+
if (!abortSignal.aborted) {
|
|
77
160
|
setState((currentState) => ({
|
|
78
161
|
...currentState,
|
|
79
162
|
isLoading: false,
|
|
80
|
-
|
|
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
|
|
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
|
-
|
|
343
|
+
isRemovePhoneNumberDialogOpen: false,
|
|
102
344
|
}));
|
|
103
345
|
}, []);
|
|
104
|
-
const
|
|
346
|
+
const completeRemovingPhoneNumber = React.useCallback(async () => {
|
|
105
347
|
setState((currentState) => ({
|
|
106
348
|
...currentState,
|
|
107
|
-
|
|
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
|
-
|
|
377
|
+
settingUpMfaMethod: mfaMethod,
|
|
378
|
+
mfaAuthenticatorAppSetup: undefined,
|
|
116
379
|
setupError: undefined,
|
|
117
380
|
}));
|
|
118
381
|
try {
|
|
119
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
438
|
+
disablingMfaMethod: undefined,
|
|
144
439
|
}));
|
|
145
440
|
}, []);
|
|
146
441
|
const completeDisablingMfa = React.useCallback(async () => {
|
|
147
|
-
|
|
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
|
-
|
|
151
|
-
|
|
451
|
+
disablingMfaMethod: undefined,
|
|
452
|
+
mfaSettings,
|
|
453
|
+
isMfaEnabled: getIsMfaEnabled(mfaSettings),
|
|
152
454
|
}));
|
|
153
455
|
}, []);
|
|
154
|
-
React.
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
|
191
|
-
* application.
|
|
192
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
549
|
+
* if (loadingError) {
|
|
550
|
+
* return <Error onRetry={refreshMfa} />
|
|
551
|
+
* }
|
|
200
552
|
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
553
|
+
* if (mfaSetupRequired) {
|
|
554
|
+
* return <ConfigureMfa />
|
|
555
|
+
* }
|
|
556
|
+
*
|
|
557
|
+
* return <Application />
|
|
204
558
|
* }
|
|
205
559
|
* ```
|
|
206
560
|
*
|
|
207
|
-
* @
|
|
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(
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return
|
|
218
|
-
|
|
219
|
-
|
|
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
|