@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
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { memo } from 'react';
3
+ import useMfa from '../../hooks/useMfa';
4
+ import ErrorSnackbar from '../ErrorSnackbar';
5
+ function MfaErrorSnackbar() {
6
+ const { setupError, clearMfaSetupError } = useMfa();
7
+ return (_jsx(ErrorSnackbar, { open: !!setupError, onClose: clearMfaSetupError, children: _jsx("span", { "data-cypress": "mfa-setup-error-message", children: setupError === null || setupError === void 0 ? void 0 : setupError.message }) }));
8
+ }
9
+ /**
10
+ * React Component that displays MFA setup errors from the `useMfa()` hook.
11
+ * Typically rendered by `<MultiFactorAuthentication />` within an `<MfaProvider
12
+ * />` tree.
13
+ *
14
+ * @returns
15
+ */
16
+ export default memo(MfaErrorSnackbar);
17
+ //# sourceMappingURL=MfaErrorSnackbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MfaErrorSnackbar.js","sourceRoot":"","sources":["../../../src/components/mfa/MfaErrorSnackbar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAC5B,OAAO,MAAM,MAAM,oBAAoB,CAAA;AACvC,OAAO,aAAa,MAAM,kBAAkB,CAAA;AAE5C,SAAS,gBAAgB;IACvB,MAAM,EAAE,UAAU,EAAE,kBAAkB,EAAE,GAAG,MAAM,EAAE,CAAA;IAEnD,OAAO,CACL,KAAC,aAAa,IAAC,IAAI,EAAE,CAAC,CAAC,UAAU,EAAE,OAAO,EAAE,kBAAkB,YAC5D,+BAAmB,yBAAyB,YAAE,UAAU,aAAV,UAAU,uBAAV,UAAU,CAAE,OAAO,GAAQ,GAC3D,CACjB,CAAA;AACH,CAAC;AAED;;;;;;GAMG;AACH,eAAe,IAAI,CAAC,gBAAgB,CAAC,CAAA","sourcesContent":["import { memo } from 'react'\nimport useMfa from '../../hooks/useMfa'\nimport ErrorSnackbar from '../ErrorSnackbar'\n\nfunction MfaErrorSnackbar() {\n const { setupError, clearMfaSetupError } = useMfa()\n\n return (\n <ErrorSnackbar open={!!setupError} onClose={clearMfaSetupError}>\n <span data-cypress=\"mfa-setup-error-message\">{setupError?.message}</span>\n </ErrorSnackbar>\n )\n}\n\n/**\n * React Component that displays MFA setup errors from the `useMfa()` hook.\n * Typically rendered by `<MultiFactorAuthentication />` within an `<MfaProvider\n * />` tree.\n *\n * @returns\n */\nexport default memo(MfaErrorSnackbar)\n"]}
@@ -0,0 +1,20 @@
1
+ type Props = {
2
+ isEnabled: boolean;
3
+ isPreferred: boolean;
4
+ isSettingUp: boolean;
5
+ isSettingPreferredMfaMethod: boolean;
6
+ isSetupDisabled: boolean;
7
+ showSetupErrorTooltip?: boolean;
8
+ title: string;
9
+ description: string;
10
+ detail?: string;
11
+ mfaRequirementMessage?: string;
12
+ cypressPrefix: string;
13
+ extraButtons?: React.ReactNode;
14
+ onSetup: () => void;
15
+ onDisable: () => void;
16
+ onSetPreferred: () => void;
17
+ };
18
+ declare function MfaMethodRow({ isEnabled, isPreferred, isSettingUp, isSettingPreferredMfaMethod, isSetupDisabled, showSetupErrorTooltip, title, description, detail, mfaRequirementMessage, cypressPrefix, extraButtons, onSetup, onDisable, onSetPreferred, }: Props): import("react/jsx-runtime").JSX.Element;
19
+ declare const _default: import("react").MemoExoticComponent<typeof MfaMethodRow>;
20
+ export default _default;
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { memo } from 'react';
3
+ import { Alert, Box, Button, Chip, Grid, Tooltip, Typography, } from '@mui/material';
4
+ function MfaMethodRow({ isEnabled, isPreferred, isSettingUp, isSettingPreferredMfaMethod, isSetupDisabled, showSetupErrorTooltip, title, description, detail, mfaRequirementMessage, cypressPrefix, extraButtons, onSetup, onDisable, onSetPreferred, }) {
5
+ return (_jsx(Box, { "data-cypress": `${cypressPrefix}-method-row`, children: _jsxs(Grid, { container: true, spacing: 2, alignItems: "center", children: [_jsxs(Grid, { size: { xs: 'grow' }, children: [_jsxs(Box, { sx: { mb: 1 }, children: [_jsx(Typography, { variant: "subtitle1", component: "span", children: title }), isEnabled && (_jsx(Chip, { size: "small", label: "Enabled", color: "info", sx: { ml: 1 }, "data-cypress": `${cypressPrefix}-status-chip` })), isPreferred && (_jsx(Chip, { size: "small", label: "Preferred", color: "success", sx: { ml: 1 }, "data-cypress": `${cypressPrefix}-preferred-chip` }))] }), _jsx(Typography, { variant: "body2", color: "text.secondary", children: description }), !!detail && (_jsx(Typography, { variant: "body2", color: "text.secondary", sx: { mt: 1 }, children: detail })), !!mfaRequirementMessage && (_jsx(Alert, { severity: isEnabled && isPreferred ? 'warning' : 'info', sx: { mt: 1 }, "data-cypress": `${cypressPrefix}-mfa-requirement-message`, children: mfaRequirementMessage }))] }), _jsx(Grid, { size: "auto", children: _jsxs(Box, { display: "flex", gap: 1, flexWrap: "wrap", justifyContent: "flex-end", children: [extraButtons, isEnabled ? (_jsxs(_Fragment, { children: [!isPreferred && (_jsx(Button, { size: "small", variant: "outlined", loading: isSettingPreferredMfaMethod, disabled: isSettingPreferredMfaMethod, onClick: onSetPreferred, "data-cypress": `${cypressPrefix}-set-preferred-button`, children: "Set as Preferred" })), _jsx(Button, { size: "small", variant: "outlined", onClick: onDisable, "data-cypress": `${cypressPrefix}-disable-button`, children: "Disable" })] })) : (_jsx(Tooltip, { title: showSetupErrorTooltip
6
+ ? 'We are unable to load your MFA status. Please try again by clicking the reload button on the chip above.'
7
+ : '', children: _jsx("span", { children: _jsx(Button, { size: "small", variant: "contained", loading: isSettingUp, disabled: isSetupDisabled, onClick: onSetup, "data-cypress": `${cypressPrefix}-setup-button`, children: "Setup" }) }) }))] }) })] }) }));
8
+ }
9
+ export default memo(MfaMethodRow);
10
+ //# sourceMappingURL=MfaMethodRow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MfaMethodRow.js","sourceRoot":"","sources":["../../../src/components/mfa/MfaMethodRow.tsx"],"names":[],"mappings":"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","sourcesContent":["import { memo } from 'react'\nimport {\n Alert,\n Box,\n Button,\n Chip,\n Grid,\n Tooltip,\n Typography,\n} from '@mui/material'\n\ntype Props = {\n isEnabled: boolean\n isPreferred: boolean\n isSettingUp: boolean\n isSettingPreferredMfaMethod: boolean\n isSetupDisabled: boolean\n showSetupErrorTooltip?: boolean\n title: string\n description: string\n detail?: string\n mfaRequirementMessage?: string\n cypressPrefix: string\n extraButtons?: React.ReactNode\n onSetup: () => void\n onDisable: () => void\n onSetPreferred: () => void\n}\n\nfunction MfaMethodRow({\n isEnabled,\n isPreferred,\n isSettingUp,\n isSettingPreferredMfaMethod,\n isSetupDisabled,\n showSetupErrorTooltip,\n title,\n description,\n detail,\n mfaRequirementMessage,\n cypressPrefix,\n extraButtons,\n onSetup,\n onDisable,\n onSetPreferred,\n}: Props) {\n return (\n <Box data-cypress={`${cypressPrefix}-method-row`}>\n <Grid container spacing={2} alignItems=\"center\">\n <Grid size={{ xs: 'grow' }}>\n <Box sx={{ mb: 1 }}>\n <Typography variant=\"subtitle1\" component=\"span\">\n {title}\n </Typography>\n {isEnabled && (\n <Chip\n size=\"small\"\n label=\"Enabled\"\n color=\"info\"\n sx={{ ml: 1 }}\n data-cypress={`${cypressPrefix}-status-chip`}\n />\n )}\n {isPreferred && (\n <Chip\n size=\"small\"\n label=\"Preferred\"\n color=\"success\"\n sx={{ ml: 1 }}\n data-cypress={`${cypressPrefix}-preferred-chip`}\n />\n )}\n </Box>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {description}\n </Typography>\n {!!detail && (\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{ mt: 1 }}>\n {detail}\n </Typography>\n )}\n {!!mfaRequirementMessage && (\n <Alert\n severity={isEnabled && isPreferred ? 'warning' : 'info'}\n sx={{ mt: 1 }}\n data-cypress={`${cypressPrefix}-mfa-requirement-message`}\n >\n {mfaRequirementMessage}\n </Alert>\n )}\n </Grid>\n <Grid size=\"auto\">\n <Box display=\"flex\" gap={1} flexWrap=\"wrap\" justifyContent=\"flex-end\">\n {extraButtons}\n {isEnabled ? (\n <>\n {!isPreferred && (\n <Button\n size=\"small\"\n variant=\"outlined\"\n loading={isSettingPreferredMfaMethod}\n disabled={isSettingPreferredMfaMethod}\n onClick={onSetPreferred}\n data-cypress={`${cypressPrefix}-set-preferred-button`}\n >\n Set as Preferred\n </Button>\n )}\n <Button\n size=\"small\"\n variant=\"outlined\"\n onClick={onDisable}\n data-cypress={`${cypressPrefix}-disable-button`}\n >\n Disable\n </Button>\n </>\n ) : (\n <Tooltip\n title={\n showSetupErrorTooltip\n ? 'We are unable to load your MFA status. Please try again by clicking the reload button on the chip above.'\n : ''\n }\n >\n <span>\n <Button\n size=\"small\"\n variant=\"contained\"\n loading={isSettingUp}\n disabled={isSetupDisabled}\n onClick={onSetup}\n data-cypress={`${cypressPrefix}-setup-button`}\n >\n Setup\n </Button>\n </span>\n </Tooltip>\n )}\n </Box>\n </Grid>\n </Grid>\n </Box>\n )\n}\n\nexport default memo(MfaMethodRow)\n"]}
@@ -0,0 +1,11 @@
1
+ import * as React from 'react';
2
+ declare function MfaPhoneNumberDialog(): import("react/jsx-runtime").JSX.Element;
3
+ /**
4
+ * React Component that lets users enter and verify a phone number for SMS MFA.
5
+ * Typically rendered by `<MultiFactorAuthentication />` within an `<MfaProvider
6
+ * />` tree.
7
+ *
8
+ * @returns
9
+ */
10
+ declare const _default: React.MemoExoticComponent<typeof MfaPhoneNumberDialog>;
11
+ export default _default;
@@ -0,0 +1,95 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import * as React from 'react';
3
+ import { Dialog, DialogActions, DialogContent, DialogTitle, Button, Typography, Box, } from '@mui/material';
4
+ import useBooleanState from '../../hooks/useBooleanState';
5
+ import useMfa from '../../hooks/useMfa';
6
+ import useResendCoolDown from '../../hooks/useResendCoolDown';
7
+ import InputField from '../InputField';
8
+ const PHONE_VERIFICATION_RESEND_COOL_DOWN_SECONDS = 60;
9
+ function MfaPhoneNumberDialog() {
10
+ const { isPhoneNumberDialogOpen, mfaSettings, phoneVerificationCodeSentAt, closePhoneNumberDialog, savePhoneNumber, verifyPhoneNumber, } = useMfa();
11
+ const isPhoneVerificationRequired = phoneVerificationCodeSentAt !== undefined;
12
+ const [phoneNumber, setPhoneNumber] = React.useState('');
13
+ const [verificationCode, setVerificationCode] = React.useState('');
14
+ const resendCoolDownSeconds = useResendCoolDown(phoneVerificationCodeSentAt, PHONE_VERIFICATION_RESEND_COOL_DOWN_SECONDS);
15
+ const [isSaving, startSaving, stopSaving] = useBooleanState(false);
16
+ React.useEffect(() => {
17
+ if (isPhoneNumberDialogOpen) {
18
+ setPhoneNumber(mfaSettings.sms.phoneNumber || '');
19
+ setVerificationCode('');
20
+ }
21
+ }, [isPhoneNumberDialogOpen, mfaSettings.sms.phoneNumber]);
22
+ const handleSavePhoneNumber = React.useCallback(async () => {
23
+ startSaving();
24
+ try {
25
+ await savePhoneNumber(phoneNumber);
26
+ }
27
+ finally {
28
+ stopSaving();
29
+ }
30
+ }, [phoneNumber, savePhoneNumber, startSaving, stopSaving]);
31
+ const handleVerifyPhoneNumber = React.useCallback(async () => {
32
+ startSaving();
33
+ try {
34
+ await verifyPhoneNumber(verificationCode);
35
+ }
36
+ finally {
37
+ stopSaving();
38
+ }
39
+ }, [verificationCode, verifyPhoneNumber, startSaving, stopSaving]);
40
+ const handleSubmit = React.useCallback((event) => {
41
+ event.preventDefault();
42
+ if (isSaving) {
43
+ return;
44
+ }
45
+ if (isPhoneVerificationRequired) {
46
+ if (!verificationCode) {
47
+ return;
48
+ }
49
+ void handleVerifyPhoneNumber();
50
+ return;
51
+ }
52
+ if (!phoneNumber) {
53
+ return;
54
+ }
55
+ void handleSavePhoneNumber();
56
+ }, [
57
+ handleSavePhoneNumber,
58
+ handleVerifyPhoneNumber,
59
+ isPhoneVerificationRequired,
60
+ isSaving,
61
+ phoneNumber,
62
+ verificationCode,
63
+ ]);
64
+ const handleResendVerificationCode = React.useCallback(async () => {
65
+ startSaving();
66
+ try {
67
+ await savePhoneNumber(phoneNumber);
68
+ }
69
+ finally {
70
+ stopSaving();
71
+ }
72
+ }, [phoneNumber, savePhoneNumber, startSaving, stopSaving]);
73
+ return (_jsx(Dialog, { open: isPhoneNumberDialogOpen, onClose: () => {
74
+ if (!isSaving) {
75
+ closePhoneNumberDialog();
76
+ }
77
+ }, fullWidth: true, maxWidth: "sm", children: _jsxs("form", { onSubmit: handleSubmit, children: [_jsx(DialogTitle, { children: isPhoneVerificationRequired
78
+ ? 'Verify Phone Number'
79
+ : 'Save Phone Number' }), _jsx(DialogContent, { dividers: true, children: isPhoneVerificationRequired ? (_jsxs(_Fragment, { children: [_jsxs(Typography, { variant: "body2", paragraph: true, children: ["Enter the verification code sent to ", phoneNumber, "."] }), _jsx(InputField, { autoFocus: true, margin: "none", name: "verificationCode", label: "Verification Code", fullWidth: true, placeholder: "XXXXXX", variant: "outlined", value: verificationCode, onChange: (event) => {
80
+ setVerificationCode(event.target.value);
81
+ }, disabled: isSaving, "data-cypress": "mfa-phone-verification-code" }, "verification-code"), _jsx(Box, { marginTop: 1, children: _jsx(Button, { type: "button", variant: "text", size: "small", sx: { textTransform: 'none' }, disabled: resendCoolDownSeconds > 0 || isSaving, onClick: handleResendVerificationCode, "data-cypress": "mfa-phone-resend-button", children: resendCoolDownSeconds > 0
82
+ ? `Send Again (${resendCoolDownSeconds}s)`
83
+ : 'Send Again' }) })] })) : (_jsxs(_Fragment, { children: [_jsx(Typography, { variant: "body2", paragraph: true, children: "Enter your phone number to receive SMS verification codes when signing in." }), _jsx(InputField, { autoFocus: true, margin: "none", name: "phoneNumber", label: "Phone Number", fullWidth: true, placeholder: "+61400000000", variant: "outlined", value: phoneNumber, onChange: (event) => {
84
+ setPhoneNumber(event.target.value);
85
+ }, disabled: isSaving, helperText: "Include your country code, for example +61400000000.", "data-cypress": "mfa-phone-number" }, "phone-number")] })) }), _jsxs(DialogActions, { children: [_jsx(Button, { type: "button", onClick: closePhoneNumberDialog, disabled: isSaving, children: "Cancel" }), isPhoneVerificationRequired ? (_jsx(Button, { type: "submit", variant: "contained", disabled: !verificationCode || isSaving, "data-cypress": "mfa-phone-verify-button", children: "Verify" })) : (_jsx(Button, { type: "submit", variant: "contained", disabled: !phoneNumber || isSaving, "data-cypress": "mfa-phone-save-button", children: "Save" }))] })] }) }));
86
+ }
87
+ /**
88
+ * React Component that lets users enter and verify a phone number for SMS MFA.
89
+ * Typically rendered by `<MultiFactorAuthentication />` within an `<MfaProvider
90
+ * />` tree.
91
+ *
92
+ * @returns
93
+ */
94
+ export default React.memo(MfaPhoneNumberDialog);
95
+ //# sourceMappingURL=MfaPhoneNumberDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MfaPhoneNumberDialog.js","sourceRoot":"","sources":["../../../src/components/mfa/MfaPhoneNumberDialog.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react'\nimport {\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Button,\n Typography,\n Box,\n} from '@mui/material'\nimport useBooleanState from '../../hooks/useBooleanState'\nimport useMfa from '../../hooks/useMfa'\nimport useResendCoolDown from '../../hooks/useResendCoolDown'\nimport InputField from '../InputField'\n\nconst PHONE_VERIFICATION_RESEND_COOL_DOWN_SECONDS = 60\n\nfunction MfaPhoneNumberDialog() {\n const {\n isPhoneNumberDialogOpen,\n mfaSettings,\n phoneVerificationCodeSentAt,\n closePhoneNumberDialog,\n savePhoneNumber,\n verifyPhoneNumber,\n } = useMfa()\n\n const isPhoneVerificationRequired = phoneVerificationCodeSentAt !== undefined\n\n const [phoneNumber, setPhoneNumber] = React.useState('')\n const [verificationCode, setVerificationCode] = React.useState('')\n const resendCoolDownSeconds = useResendCoolDown(\n phoneVerificationCodeSentAt,\n PHONE_VERIFICATION_RESEND_COOL_DOWN_SECONDS,\n )\n const [isSaving, startSaving, stopSaving] = useBooleanState(false)\n\n React.useEffect(() => {\n if (isPhoneNumberDialogOpen) {\n setPhoneNumber(mfaSettings.sms.phoneNumber || '')\n setVerificationCode('')\n }\n }, [isPhoneNumberDialogOpen, mfaSettings.sms.phoneNumber])\n\n const handleSavePhoneNumber = React.useCallback(async () => {\n startSaving()\n try {\n await savePhoneNumber(phoneNumber)\n } finally {\n stopSaving()\n }\n }, [phoneNumber, savePhoneNumber, startSaving, stopSaving])\n\n const handleVerifyPhoneNumber = React.useCallback(async () => {\n startSaving()\n try {\n await verifyPhoneNumber(verificationCode)\n } finally {\n stopSaving()\n }\n }, [verificationCode, verifyPhoneNumber, startSaving, stopSaving])\n\n const handleSubmit = React.useCallback(\n (event: React.SubmitEvent<HTMLFormElement>) => {\n event.preventDefault()\n\n if (isSaving) {\n return\n }\n\n if (isPhoneVerificationRequired) {\n if (!verificationCode) {\n return\n }\n\n void handleVerifyPhoneNumber()\n return\n }\n\n if (!phoneNumber) {\n return\n }\n\n void handleSavePhoneNumber()\n },\n [\n handleSavePhoneNumber,\n handleVerifyPhoneNumber,\n isPhoneVerificationRequired,\n isSaving,\n phoneNumber,\n verificationCode,\n ],\n )\n\n const handleResendVerificationCode = React.useCallback(async () => {\n startSaving()\n try {\n await savePhoneNumber(phoneNumber)\n } finally {\n stopSaving()\n }\n }, [phoneNumber, savePhoneNumber, startSaving, stopSaving])\n\n return (\n <Dialog\n open={isPhoneNumberDialogOpen}\n onClose={() => {\n if (!isSaving) {\n closePhoneNumberDialog()\n }\n }}\n fullWidth\n maxWidth=\"sm\"\n >\n <form onSubmit={handleSubmit}>\n <DialogTitle>\n {isPhoneVerificationRequired\n ? 'Verify Phone Number'\n : 'Save Phone Number'}\n </DialogTitle>\n <DialogContent dividers>\n {isPhoneVerificationRequired ? (\n <>\n <Typography variant=\"body2\" paragraph>\n Enter the verification code sent to {phoneNumber}.\n </Typography>\n <InputField\n key=\"verification-code\"\n autoFocus\n margin=\"none\"\n name=\"verificationCode\"\n label=\"Verification Code\"\n fullWidth\n placeholder=\"XXXXXX\"\n variant=\"outlined\"\n value={verificationCode}\n onChange={(event) => {\n setVerificationCode(event.target.value)\n }}\n disabled={isSaving}\n data-cypress=\"mfa-phone-verification-code\"\n />\n <Box marginTop={1}>\n <Button\n type=\"button\"\n variant=\"text\"\n size=\"small\"\n sx={{ textTransform: 'none' }}\n disabled={resendCoolDownSeconds > 0 || isSaving}\n onClick={handleResendVerificationCode}\n data-cypress=\"mfa-phone-resend-button\"\n >\n {resendCoolDownSeconds > 0\n ? `Send Again (${resendCoolDownSeconds}s)`\n : 'Send Again'}\n </Button>\n </Box>\n </>\n ) : (\n <>\n <Typography variant=\"body2\" paragraph>\n Enter your phone number to receive SMS verification codes when\n signing in.\n </Typography>\n <InputField\n key=\"phone-number\"\n autoFocus\n margin=\"none\"\n name=\"phoneNumber\"\n label=\"Phone Number\"\n fullWidth\n placeholder=\"+61400000000\"\n variant=\"outlined\"\n value={phoneNumber}\n onChange={(event) => {\n setPhoneNumber(event.target.value)\n }}\n disabled={isSaving}\n helperText=\"Include your country code, for example +61400000000.\"\n data-cypress=\"mfa-phone-number\"\n />\n </>\n )}\n </DialogContent>\n <DialogActions>\n <Button\n type=\"button\"\n onClick={closePhoneNumberDialog}\n disabled={isSaving}\n >\n Cancel\n </Button>\n {isPhoneVerificationRequired ? (\n <Button\n type=\"submit\"\n variant=\"contained\"\n disabled={!verificationCode || isSaving}\n data-cypress=\"mfa-phone-verify-button\"\n >\n Verify\n </Button>\n ) : (\n <Button\n type=\"submit\"\n variant=\"contained\"\n disabled={!phoneNumber || isSaving}\n data-cypress=\"mfa-phone-save-button\"\n >\n Save\n </Button>\n )}\n </DialogActions>\n </form>\n </Dialog>\n )\n}\n\n/**\n * React Component that lets users enter and verify a phone number for SMS MFA.\n * Typically rendered by `<MultiFactorAuthentication />` within an `<MfaProvider\n * />` tree.\n *\n * @returns\n */\nexport default React.memo(MfaPhoneNumberDialog)\n"]}
@@ -0,0 +1,10 @@
1
+ declare function MfaRemovePhoneNumberDialog(): import("react/jsx-runtime").JSX.Element;
2
+ /**
3
+ * React Component that prompts the user to confirm removing their phone number.
4
+ * Typically rendered by `<MultiFactorAuthentication />` within an `<MfaProvider
5
+ * />` tree.
6
+ *
7
+ * @returns
8
+ */
9
+ declare const _default: import("react").MemoExoticComponent<typeof MfaRemovePhoneNumberDialog>;
10
+ export default _default;
@@ -0,0 +1,24 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { memo } from 'react';
3
+ import { Typography } from '@mui/material';
4
+ import ConfirmDialog from '../ConfirmDialog';
5
+ import MaterialIcon from '../MaterialIcon';
6
+ import useMfa from '../../hooks/useMfa';
7
+ function MfaRemovePhoneNumberDialog() {
8
+ const { isRemovePhoneNumberDialogOpen, mfaSettings, completeRemovingPhoneNumber, cancelRemovingPhoneNumber, } = useMfa();
9
+ return (_jsx(ConfirmDialog, { isOpen: isRemovePhoneNumberDialogOpen, onClose: cancelRemovingPhoneNumber, onConfirm: completeRemovingPhoneNumber, title: "Please Confirm", confirmButtonText: "Remove Phone Number", confirmButtonIcon: _jsx(MaterialIcon, { children: "phone_disabled" }), cypress: {
10
+ dialog: 'remove-phone-number-dialog',
11
+ confirmButton: 'remove-phone-number-dialog-confirm-button',
12
+ cancelButton: 'remove-phone-number-dialog-cancel-button',
13
+ error: 'remove-phone-number-dialog-error-message',
14
+ }, children: _jsxs(Typography, { variant: "body2", children: ["Are you sure you want to remove ", mfaSettings.sms.phoneNumber, " from your account?"] }) }));
15
+ }
16
+ /**
17
+ * React Component that prompts the user to confirm removing their phone number.
18
+ * Typically rendered by `<MultiFactorAuthentication />` within an `<MfaProvider
19
+ * />` tree.
20
+ *
21
+ * @returns
22
+ */
23
+ export default memo(MfaRemovePhoneNumberDialog);
24
+ //# sourceMappingURL=MfaRemovePhoneNumberDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MfaRemovePhoneNumberDialog.js","sourceRoot":"","sources":["../../../src/components/mfa/MfaRemovePhoneNumberDialog.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,aAAa,MAAM,kBAAkB,CAAA;AAC5C,OAAO,YAAY,MAAM,iBAAiB,CAAA;AAC1C,OAAO,MAAM,MAAM,oBAAoB,CAAA;AAEvC,SAAS,0BAA0B;IACjC,MAAM,EACJ,6BAA6B,EAC7B,WAAW,EACX,2BAA2B,EAC3B,yBAAyB,GAC1B,GAAG,MAAM,EAAE,CAAA;IAEZ,OAAO,CACL,KAAC,aAAa,IACZ,MAAM,EAAE,6BAA6B,EACrC,OAAO,EAAE,yBAAyB,EAClC,SAAS,EAAE,2BAA2B,EACtC,KAAK,EAAC,gBAAgB,EACtB,iBAAiB,EAAC,qBAAqB,EACvC,iBAAiB,EAAE,KAAC,YAAY,iCAA8B,EAC9D,OAAO,EAAE;YACP,MAAM,EAAE,4BAA4B;YACpC,aAAa,EAAE,2CAA2C;YAC1D,YAAY,EAAE,0CAA0C;YACxD,KAAK,EAAE,0CAA0C;SAClD,YAED,MAAC,UAAU,IAAC,OAAO,EAAC,OAAO,iDACQ,WAAW,CAAC,GAAG,CAAC,WAAW,2BAEjD,GACC,CACjB,CAAA;AACH,CAAC;AAED;;;;;;GAMG;AACH,eAAe,IAAI,CAAC,0BAA0B,CAAC,CAAA","sourcesContent":["import { memo } from 'react'\r\nimport { Typography } from '@mui/material'\r\nimport ConfirmDialog from '../ConfirmDialog'\r\nimport MaterialIcon from '../MaterialIcon'\r\nimport useMfa from '../../hooks/useMfa'\r\n\r\nfunction MfaRemovePhoneNumberDialog() {\r\n const {\r\n isRemovePhoneNumberDialogOpen,\r\n mfaSettings,\r\n completeRemovingPhoneNumber,\r\n cancelRemovingPhoneNumber,\r\n } = useMfa()\r\n\r\n return (\r\n <ConfirmDialog\r\n isOpen={isRemovePhoneNumberDialogOpen}\r\n onClose={cancelRemovingPhoneNumber}\r\n onConfirm={completeRemovingPhoneNumber}\r\n title=\"Please Confirm\"\r\n confirmButtonText=\"Remove Phone Number\"\r\n confirmButtonIcon={<MaterialIcon>phone_disabled</MaterialIcon>}\r\n cypress={{\r\n dialog: 'remove-phone-number-dialog',\r\n confirmButton: 'remove-phone-number-dialog-confirm-button',\r\n cancelButton: 'remove-phone-number-dialog-cancel-button',\r\n error: 'remove-phone-number-dialog-error-message',\r\n }}\r\n >\r\n <Typography variant=\"body2\">\r\n Are you sure you want to remove {mfaSettings.sms.phoneNumber} from your\r\n account?\r\n </Typography>\r\n </ConfirmDialog>\r\n )\r\n}\r\n\r\n/**\r\n * React Component that prompts the user to confirm removing their phone number.\r\n * Typically rendered by `<MultiFactorAuthentication />` within an `<MfaProvider\r\n * />` tree.\r\n *\r\n * @returns\r\n */\r\nexport default memo(MfaRemovePhoneNumberDialog)\r\n"]}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * React Component that displays a status chip for the user's MFA status.
3
+ * Typically rendered by `<MultiFactorAuthentication />` within an `<MfaProvider
4
+ * />` tree.
5
+ *
6
+ * @returns
7
+ */
8
+ declare function MfaStatusChip(): import("react/jsx-runtime").JSX.Element | null;
9
+ declare const _default: import("react").MemoExoticComponent<typeof MfaStatusChip>;
10
+ export default _default;
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { memo } from 'react';
3
+ import { Chip, CircularProgress, Tooltip } from '@mui/material';
4
+ import MaterialIcon from '../MaterialIcon';
5
+ import useMfa from '../../hooks/useMfa';
6
+ /**
7
+ * React Component that displays a status chip for the user's MFA status.
8
+ * Typically rendered by `<MultiFactorAuthentication />` within an `<MfaProvider
9
+ * />` tree.
10
+ *
11
+ * @returns
12
+ */
13
+ function MfaStatusChip() {
14
+ const { loadingError, isLoading, isMfaEnabled, loadMfa, isExternalIdentityProviderUser, } = useMfa();
15
+ if (isExternalIdentityProviderUser)
16
+ return null;
17
+ if (isLoading) {
18
+ return (_jsx(Chip, { label: "Loading MFA Status...", icon: _jsx(CircularProgress, { size: 16 }) }));
19
+ }
20
+ if (loadingError) {
21
+ return (_jsx(Tooltip, { title: loadingError.message, children: _jsx(Chip, { label: "MFA Loading Error", icon: _jsx(MaterialIcon, { color: "error", children: "error" }), deleteIcon: _jsx(MaterialIcon, { color: "error", children: "refresh" }), onDelete: loadMfa }) }));
22
+ }
23
+ if (isMfaEnabled) {
24
+ return (_jsx(Chip, { label: "Enabled", icon: _jsx(MaterialIcon, { color: "success", children: "verified_user" }), deleteIcon: _jsx(MaterialIcon, { color: "error", children: "refresh" }), onDelete: loadMfa }));
25
+ }
26
+ return (_jsx(Chip, { label: "Disabled", icon: _jsx(MaterialIcon, { color: "warning", children: "remove_moderator" }), deleteIcon: _jsx(MaterialIcon, { color: "error", children: "refresh" }), onDelete: loadMfa }));
27
+ }
28
+ export default memo(MfaStatusChip);
29
+ //# sourceMappingURL=MfaStatusChip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MfaStatusChip.js","sourceRoot":"","sources":["../../../src/components/mfa/MfaStatusChip.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAC5B,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAC/D,OAAO,YAAY,MAAM,iBAAiB,CAAA;AAC1C,OAAO,MAAM,MAAM,oBAAoB,CAAA;AAEvC;;;;;;GAMG;AACH,SAAS,aAAa;IACpB,MAAM,EACJ,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,OAAO,EACP,8BAA8B,GAC/B,GAAG,MAAM,EAAE,CAAA;IAEZ,IAAI,8BAA8B;QAAE,OAAO,IAAI,CAAA;IAE/C,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,CACL,KAAC,IAAI,IACH,KAAK,EAAC,uBAAuB,EAC7B,IAAI,EAAE,KAAC,gBAAgB,IAAC,IAAI,EAAE,EAAE,GAAI,GACpC,CACH,CAAA;IACH,CAAC;IAED,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO,CACL,KAAC,OAAO,IAAC,KAAK,EAAE,YAAY,CAAC,OAAO,YAClC,KAAC,IAAI,IACH,KAAK,EAAC,mBAAmB,EACzB,IAAI,EAAE,KAAC,YAAY,IAAC,KAAK,EAAC,OAAO,sBAAqB,EACtD,UAAU,EAAE,KAAC,YAAY,IAAC,KAAK,EAAC,OAAO,wBAAuB,EAC9D,QAAQ,EAAE,OAAO,GACjB,GACM,CACX,CAAA;IACH,CAAC;IAED,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO,CACL,KAAC,IAAI,IACH,KAAK,EAAC,SAAS,EACf,IAAI,EAAE,KAAC,YAAY,IAAC,KAAK,EAAC,SAAS,8BAA6B,EAChE,UAAU,EAAE,KAAC,YAAY,IAAC,KAAK,EAAC,OAAO,wBAAuB,EAC9D,QAAQ,EAAE,OAAO,GACjB,CACH,CAAA;IACH,CAAC;IAED,OAAO,CACL,KAAC,IAAI,IACH,KAAK,EAAC,UAAU,EAChB,IAAI,EAAE,KAAC,YAAY,IAAC,KAAK,EAAC,SAAS,iCAAgC,EACnE,UAAU,EAAE,KAAC,YAAY,IAAC,KAAK,EAAC,OAAO,wBAAuB,EAC9D,QAAQ,EAAE,OAAO,GACjB,CACH,CAAA;AACH,CAAC;AAED,eAAe,IAAI,CAAC,aAAa,CAAC,CAAA","sourcesContent":["import { memo } from 'react'\nimport { Chip, CircularProgress, Tooltip } from '@mui/material'\nimport MaterialIcon from '../MaterialIcon'\nimport useMfa from '../../hooks/useMfa'\n\n/**\n * React Component that displays a status chip for the user's MFA status.\n * Typically rendered by `<MultiFactorAuthentication />` within an `<MfaProvider\n * />` tree.\n *\n * @returns\n */\nfunction MfaStatusChip() {\n const {\n loadingError,\n isLoading,\n isMfaEnabled,\n loadMfa,\n isExternalIdentityProviderUser,\n } = useMfa()\n\n if (isExternalIdentityProviderUser) return null\n\n if (isLoading) {\n return (\n <Chip\n label=\"Loading MFA Status...\"\n icon={<CircularProgress size={16} />}\n />\n )\n }\n\n if (loadingError) {\n return (\n <Tooltip title={loadingError.message}>\n <Chip\n label=\"MFA Loading Error\"\n icon={<MaterialIcon color=\"error\">error</MaterialIcon>}\n deleteIcon={<MaterialIcon color=\"error\">refresh</MaterialIcon>}\n onDelete={loadMfa}\n />\n </Tooltip>\n )\n }\n\n if (isMfaEnabled) {\n return (\n <Chip\n label=\"Enabled\"\n icon={<MaterialIcon color=\"success\">verified_user</MaterialIcon>}\n deleteIcon={<MaterialIcon color=\"error\">refresh</MaterialIcon>}\n onDelete={loadMfa}\n />\n )\n }\n\n return (\n <Chip\n label=\"Disabled\"\n icon={<MaterialIcon color=\"warning\">remove_moderator</MaterialIcon>}\n deleteIcon={<MaterialIcon color=\"error\">refresh</MaterialIcon>}\n onDelete={loadMfa}\n />\n )\n}\n\nexport default memo(MfaStatusChip)\n"]}
@@ -0,0 +1,10 @@
1
+ declare function MfaSuccessSnackbar(): import("react/jsx-runtime").JSX.Element;
2
+ /**
3
+ * React Component that displays a success snackbar when MFA setup completes.
4
+ * Typically rendered by `<MultiFactorAuthentication />` within an `<MfaProvider
5
+ * />` tree.
6
+ *
7
+ * @returns
8
+ */
9
+ declare const _default: import("react").MemoExoticComponent<typeof MfaSuccessSnackbar>;
10
+ export default _default;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { memo } from 'react';
3
+ import useMfa from '../../hooks/useMfa';
4
+ import SuccessSnackbar from '../SuccessSnackbar';
5
+ function MfaSuccessSnackbar() {
6
+ const { isSetupSuccessOpen, hideSetupSuccess } = useMfa();
7
+ return (_jsx(SuccessSnackbar, { open: isSetupSuccessOpen, onClose: hideSetupSuccess, children: "MFA has been successfully setup." }));
8
+ }
9
+ /**
10
+ * React Component that displays a success snackbar when MFA setup completes.
11
+ * Typically rendered by `<MultiFactorAuthentication />` within an `<MfaProvider
12
+ * />` tree.
13
+ *
14
+ * @returns
15
+ */
16
+ export default memo(MfaSuccessSnackbar);
17
+ //# sourceMappingURL=MfaSuccessSnackbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MfaSuccessSnackbar.js","sourceRoot":"","sources":["../../../src/components/mfa/MfaSuccessSnackbar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAC5B,OAAO,MAAM,MAAM,oBAAoB,CAAA;AACvC,OAAO,eAAe,MAAM,oBAAoB,CAAA;AAEhD,SAAS,kBAAkB;IACzB,MAAM,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,GAAG,MAAM,EAAE,CAAA;IAEzD,OAAO,CACL,KAAC,eAAe,IAAC,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,gBAAgB,iDAElD,CACnB,CAAA;AACH,CAAC;AAED;;;;;;GAMG;AACH,eAAe,IAAI,CAAC,kBAAkB,CAAC,CAAA","sourcesContent":["import { memo } from 'react'\nimport useMfa from '../../hooks/useMfa'\nimport SuccessSnackbar from '../SuccessSnackbar'\n\nfunction MfaSuccessSnackbar() {\n const { isSetupSuccessOpen, hideSetupSuccess } = useMfa()\n\n return (\n <SuccessSnackbar open={isSetupSuccessOpen} onClose={hideSetupSuccess}>\n MFA has been successfully setup.\n </SuccessSnackbar>\n )\n}\n\n/**\n * React Component that displays a success snackbar when MFA setup completes.\n * Typically rendered by `<MultiFactorAuthentication />` within an `<MfaProvider\n * />` tree.\n *\n * @returns\n */\nexport default memo(MfaSuccessSnackbar)\n"]}
@@ -1,5 +1,6 @@
1
- export declare const LargeIcon: import("@emotion/styled").StyledComponent<import("@mui/material").IconOwnProps & import("@mui/material/OverridableComponent").CommonProps & Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "className" | "children" | "style" | "classes" | "sx" | "color" | "fontSize" | "baseClassName"> & import("@mui/system").MUIStyledCommonProps<import("@mui/material").Theme>, {}, {}>;
1
+ import { MiscTypes } from '@oneblink/types';
2
2
  type Props = {
3
+ mfaRequirement: MiscTypes.MfaRequirement | undefined;
3
4
  ssoSetupUrl?: string;
4
5
  };
5
6
  /**
@@ -17,26 +18,25 @@ type Props = {
17
18
  * } from '@oneblink/apps-react'
18
19
  *
19
20
  * function Component() {
20
- * return <MultiFactorAuthentication />
21
+ * return <MultiFactorAuthentication mfaRequirement={undefined} />
21
22
  * }
22
23
  *
23
- * function App() {
24
+ * function AppWithMfaRequirement({ mfaRequirement }) {
24
25
  * return (
25
26
  * <MfaProvider>
26
- * <Component />
27
+ * <MultiFactorAuthentication mfaRequirement={mfaRequirement} />
27
28
  * </MfaProvider>
28
29
  * )
29
30
  * }
30
- *
31
- * const root = document.getElementById('root')
32
- * if (root) {
33
- * ReactDOM.render(<App />, root)
34
- * }
35
31
  * ```
36
32
  *
37
33
  * @param props
34
+ * @param props.mfaRequirement - The MFA methods allowed by your administrator
35
+ * for using this app. Pass `undefined` when the app has no MFA requirement.
36
+ * Users can still enable other methods, but will be warned when their
37
+ * configuration does not meet this requirement.
38
38
  * @returns
39
39
  * @group Components
40
40
  */
41
- export default function MultiFactorAuthentication({ ssoSetupUrl }: Props): import("react/jsx-runtime").JSX.Element;
41
+ export default function MultiFactorAuthentication({ mfaRequirement, ssoSetupUrl, }: Props): import("react/jsx-runtime").JSX.Element;
42
42
  export {};
@@ -1,42 +1,50 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Box, Button, Chip, CircularProgress, Divider, Grid, Icon, Paper, Tooltip, Typography, styled, } from '@mui/material';
3
- import ConfirmDialog from '../ConfirmDialog';
4
- import MfaDialog from './MfaDialog';
5
- import ErrorSnackbar from '../ErrorSnackbar';
6
- import MaterialIcon from '../MaterialIcon';
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ import { Box, Button, Divider, Grid, Paper, Tooltip, Typography, } from '@mui/material';
4
+ import MfaAuthenticatorAppDialog from './MfaAuthenticatorAppDialog';
7
5
  import useMfa from '../../hooks/useMfa';
8
- import ErrorMessage from '../messages/ErrorMessage';
9
- export const LargeIcon = styled(Icon)(({ theme }) => ({
10
- fontSize: `${theme.typography.h4.fontSize} !important`,
11
- }));
12
- function MfaStatus({ isExternalIdentityProviderUser, isLoading, loadingError, loadMfa, isMfaEnabled, }) {
13
- if (isExternalIdentityProviderUser)
14
- return null;
6
+ import MfaDisableDialog from './MfaDisableDialog';
7
+ import MfaRemovePhoneNumberDialog from './MfaRemovePhoneNumberDialog';
8
+ import MfaPhoneNumberDialog from './MfaPhoneNumberDialog';
9
+ import MfaMethodRow from './MfaMethodRow';
10
+ import MfaSuccessSnackbar from './MfaSuccessSnackbar';
11
+ import MfaErrorSnackbar from './MfaErrorSnackbar';
12
+ import MfaStatusChip from './MfaStatusChip';
13
+ import { formatMfaMethodNotAcceptedMessage } from '../../utils/mfa-requirement';
14
+ function MfaMethodList({ mfaRequirement }) {
15
+ const { mfaSettings, loadingError, isLoading, isSettingUpMfa, settingUpMfaMethod, isSettingPreferredMfaMethod, beginMfaSetup, beginDisablingMfaMethod, setPreferredMfaMethod, beginRemovingPhoneNumber, } = useMfa();
16
+ const phoneDetail = useMemo(() => {
17
+ if (!mfaSettings.sms.phoneNumber) {
18
+ return undefined;
19
+ }
20
+ if (!mfaSettings.sms.isPhoneNumberVerified) {
21
+ return `Phone number: ${mfaSettings.sms.phoneNumber} (not verified)`;
22
+ }
23
+ return `Phone number: ${mfaSettings.sms.phoneNumber}`;
24
+ }, [mfaSettings]);
25
+ const authenticatorMfaRequirementMessage = useMemo(() => {
26
+ return formatMfaMethodNotAcceptedMessage('authenticatorApp', mfaRequirement);
27
+ }, [mfaRequirement]);
28
+ const smsMfaRequirementMessage = useMemo(() => {
29
+ return formatMfaMethodNotAcceptedMessage('sms', mfaRequirement);
30
+ }, [mfaRequirement]);
15
31
  if (isLoading) {
16
- return (_jsx(Box, { padding: 3, children: _jsx(Grid, { container: true, justifyContent: "center", children: _jsx(CircularProgress, {}) }) }));
32
+ return (_jsx(Typography, { variant: "body2", color: "text.secondary", children: "Loading MFA methods..." }));
17
33
  }
18
34
  if (loadingError) {
19
- return (_jsx("div", { children: _jsx(ErrorMessage, { title: "Error Loading Multi Factor Authentication Configuration", onTryAgain: loadMfa, children: loadingError.message }) }));
35
+ return (_jsx(Typography, { variant: "body2", color: "error", children: "We are unable to load your MFA methods. Please try again by clicking the reload button on the chip above." }));
20
36
  }
21
- if (isMfaEnabled) {
22
- return (_jsx(Chip, { label: "Enabled", icon: _jsx(MaterialIcon, { color: "success", children: "verified_user" }) }));
23
- }
24
- return (_jsx(Chip, { label: "Disabled", icon: _jsx(MaterialIcon, { color: "warning", children: "remove_moderator" }) }));
37
+ return (_jsxs(_Fragment, { children: [_jsx(MfaMethodRow, { isEnabled: mfaSettings.authenticator.enabled, isPreferred: mfaSettings.authenticator.preferred, isSettingUp: isSettingUpMfa && settingUpMfaMethod === 'authenticator', isSettingPreferredMfaMethod: isSettingPreferredMfaMethod, isSetupDisabled: !!loadingError || isSettingUpMfa, showSetupErrorTooltip: !!loadingError, title: "Authenticator App", description: "Use an app like Google Authenticator or Microsoft Authenticator to generate 6-digit verification codes.", mfaRequirementMessage: authenticatorMfaRequirementMessage, cypressPrefix: "mfa-authenticator", onSetup: () => beginMfaSetup('authenticator'), onDisable: () => beginDisablingMfaMethod('authenticator'), onSetPreferred: () => setPreferredMfaMethod('authenticator') }), _jsx(Divider, { sx: { my: 2 } }), _jsx(MfaMethodRow, { isEnabled: mfaSettings.sms.enabled, isPreferred: mfaSettings.sms.preferred, isSettingUp: isSettingUpMfa && settingUpMfaMethod === 'sms', isSettingPreferredMfaMethod: isSettingPreferredMfaMethod, isSetupDisabled: !!loadingError || isSettingUpMfa, showSetupErrorTooltip: !!loadingError, title: "SMS", description: "Receive a one-time verification code via SMS each time MFA is required.", detail: phoneDetail, mfaRequirementMessage: smsMfaRequirementMessage, cypressPrefix: "mfa-sms", onSetup: () => beginMfaSetup('sms'), onDisable: () => beginDisablingMfaMethod('sms'), onSetPreferred: () => setPreferredMfaMethod('sms'), extraButtons: !!mfaSettings.sms.phoneNumber && !mfaSettings.sms.enabled ? (_jsx(Button, { size: "small", variant: "outlined", disabled: !!loadingError, onClick: beginRemovingPhoneNumber, "data-cypress": "mfa-sms-remove-phone-button", children: "Remove Phone" })) : undefined })] }));
25
38
  }
26
- function MfaSetup({ ssoSetupUrl }) {
27
- const { setupError, isMfaEnabled, isDisablingMfa, isSettingUpMfa, mfaSetup, beginMfaSetup, cancelMfaSetup, completeMfaSetup, clearMfaSetupError, beginDisablingMfa, completeDisablingMfa, cancelDisablingMfa, isExternalIdentityProviderUser, } = useMfa();
39
+ function MfaSetup({ ssoSetupUrl, mfaRequirement }) {
40
+ const { isExternalIdentityProviderUser } = useMfa();
28
41
  if (ssoSetupUrl) {
29
- return (_jsx(Grid, { children: _jsx(Button, { variant: "outlined", size: "small", component: "a", href: ssoSetupUrl, target: "_blank", rel: "noopener noreferrer", "data-cypress": "configure-mfa-button", children: "Configure MFA" }) }));
42
+ return (_jsx(Button, { variant: "outlined", size: "small", component: "a", href: ssoSetupUrl, target: "_blank", rel: "noopener noreferrer", "data-cypress": "configure-mfa-button", children: "Configure MFA" }));
30
43
  }
31
44
  if (isExternalIdentityProviderUser) {
32
- return (_jsx(Grid, { children: _jsx(Tooltip, { title: "MFA must be configured in your login provider.", children: _jsx("span", { children: _jsx(Button, { variant: "outlined", size: "small", disabled: isExternalIdentityProviderUser, "data-cypress": "configure-mfa-button", children: "Configure MFA" }) }) }) }));
45
+ return (_jsx(Tooltip, { title: "MFA must be configured in your login provider.", children: _jsx("span", { children: _jsx(Button, { variant: "outlined", disabled: isExternalIdentityProviderUser, "data-cypress": "configure-mfa-button", children: "Configure MFA" }) }) }));
33
46
  }
34
- return (_jsxs(_Fragment, { children: [_jsxs(Grid, { children: [_jsx(Button, { variant: "outlined", size: "small", disabled: !isMfaEnabled, "data-cypress": "disable-mfa-button", onClick: beginDisablingMfa, children: "Disable MFA" }), _jsx(ConfirmDialog, { isOpen: isDisablingMfa, onClose: cancelDisablingMfa, onConfirm: completeDisablingMfa, title: "Please Confirm", confirmButtonText: "Disable MFA", confirmButtonIcon: _jsx(MaterialIcon, { children: "remove_moderator" }), cypress: {
35
- dialog: 'disable-mfa-dialog',
36
- confirmButton: 'disable-mfa-dialog-confirm-button',
37
- cancelButton: 'disable-mfa-dialog-cancel-button',
38
- error: 'disable-mfa-dialog-error-message',
39
- }, children: _jsx(Typography, { variant: "body2", children: "Are you sure want to disable multi factor authentication (MFA)?" }) })] }), _jsxs(Grid, { children: [_jsx(Button, { variant: "contained", size: "small", loading: isSettingUpMfa, disabled: isMfaEnabled, onClick: beginMfaSetup, "data-cypress": "setup-mfa-button", children: "Setup MFA" }), _jsx(MfaDialog, { mfaSetup: mfaSetup, onClose: cancelMfaSetup, onCompleted: completeMfaSetup })] }), _jsx(ErrorSnackbar, { open: !!setupError, onClose: clearMfaSetupError, children: _jsx("span", { "data-cypress": "mfa-setup-error-message", children: setupError === null || setupError === void 0 ? void 0 : setupError.message }) })] }));
47
+ return (_jsxs(_Fragment, { children: [_jsx(MfaMethodList, { mfaRequirement: mfaRequirement }), _jsx(MfaDisableDialog, {}), _jsx(MfaRemovePhoneNumberDialog, {}), _jsx(MfaPhoneNumberDialog, {}), _jsx(MfaAuthenticatorAppDialog, {}), _jsx(MfaErrorSnackbar, {}), _jsx(MfaSuccessSnackbar, {})] }));
40
48
  }
41
49
  /**
42
50
  * React Component that provides a mechanism for app users to configure Multi
@@ -53,29 +61,27 @@ function MfaSetup({ ssoSetupUrl }) {
53
61
  * } from '@oneblink/apps-react'
54
62
  *
55
63
  * function Component() {
56
- * return <MultiFactorAuthentication />
64
+ * return <MultiFactorAuthentication mfaRequirement={undefined} />
57
65
  * }
58
66
  *
59
- * function App() {
67
+ * function AppWithMfaRequirement({ mfaRequirement }) {
60
68
  * return (
61
69
  * <MfaProvider>
62
- * <Component />
70
+ * <MultiFactorAuthentication mfaRequirement={mfaRequirement} />
63
71
  * </MfaProvider>
64
72
  * )
65
73
  * }
66
- *
67
- * const root = document.getElementById('root')
68
- * if (root) {
69
- * ReactDOM.render(<App />, root)
70
- * }
71
74
  * ```
72
75
  *
73
76
  * @param props
77
+ * @param props.mfaRequirement - The MFA methods allowed by your administrator
78
+ * for using this app. Pass `undefined` when the app has no MFA requirement.
79
+ * Users can still enable other methods, but will be warned when their
80
+ * configuration does not meet this requirement.
74
81
  * @returns
75
82
  * @group Components
76
83
  */
77
- export default function MultiFactorAuthentication({ ssoSetupUrl }) {
78
- const { loadingError, isLoading, isMfaEnabled, loadMfa, isExternalIdentityProviderUser, } = useMfa();
79
- return (_jsx(Grid, { size: { xs: 'grow', lg: 8 }, children: _jsx(Box, { padding: 3, children: _jsx(Paper, { children: _jsx(Box, { padding: 3, children: _jsx(Grid, { container: true, spacing: 2, alignItems: "center", children: _jsxs(Grid, { size: { xs: 'grow' }, children: [_jsxs(Typography, { variant: "h4", fontWeight: "light", children: ["Multi Factor Authentication", ' ', _jsx(MfaStatus, { loadMfa: loadMfa, isLoading: isLoading, loadingError: loadingError, isMfaEnabled: isMfaEnabled, isExternalIdentityProviderUser: !!isExternalIdentityProviderUser })] }), _jsx(Box, { marginY: 1, children: _jsx(Divider, {}) }), _jsx(Typography, { variant: "body2", paragraph: true, children: "Multi factor authentication (MFA), also known as two factor authentication (2FA), is a best practice that requires a second authentication factor in addition to user name and password sign-in credentials. We strongly recommend enabling MFA to enhance your account security." }), _jsx(Grid, { container: true, justifyContent: "flex-end", spacing: 1, children: _jsx(MfaSetup, { ssoSetupUrl: ssoSetupUrl || '' }) })] }) }) }) }) }) }));
84
+ export default function MultiFactorAuthentication({ mfaRequirement, ssoSetupUrl, }) {
85
+ return (_jsx(Grid, { size: { xs: 'grow', lg: 8 }, children: _jsx(Box, { padding: 3, children: _jsx(Paper, { children: _jsx(Box, { padding: 3, children: _jsx(Grid, { container: true, spacing: 2, alignItems: "center", children: _jsxs(Grid, { size: { xs: 'grow' }, children: [_jsxs(Typography, { variant: "h4", fontWeight: "light", children: ["Multi Factor Authentication ", _jsx(MfaStatusChip, {})] }), _jsx(Box, { marginY: 1, children: _jsx(Divider, {}) }), _jsx(Typography, { variant: "body2", paragraph: true, children: "Multi factor authentication (MFA), also known as two factor authentication (2FA), is a best practice that requires a second authentication factor in addition to user name and password sign-in credentials. We strongly recommend enabling MFA to enhance your account security." }), _jsx(MfaSetup, { ssoSetupUrl: ssoSetupUrl, mfaRequirement: mfaRequirement })] }) }) }) }) }) }));
80
86
  }
81
87
  //# sourceMappingURL=MultiFactorAuthentication.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MultiFactorAuthentication.js","sourceRoot":"","sources":["../../../src/components/mfa/MultiFactorAuthentication.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n Box,\n Button,\n Chip,\n CircularProgress,\n Divider,\n Grid,\n Icon,\n Paper,\n Tooltip,\n Typography,\n styled,\n} from '@mui/material'\nimport ConfirmDialog from '../ConfirmDialog'\nimport MfaDialog from './MfaDialog'\nimport ErrorSnackbar from '../ErrorSnackbar'\nimport MaterialIcon from '../MaterialIcon'\nimport useMfa from '../../hooks/useMfa'\nimport ErrorMessage from '../messages/ErrorMessage'\n\nexport const LargeIcon = styled(Icon)(({ theme }) => ({\n fontSize: `${theme.typography.h4.fontSize} !important`,\n}))\n\ntype Props = {\n ssoSetupUrl?: string\n}\n\ntype MfaStatusProps = {\n isExternalIdentityProviderUser: boolean\n isLoading: boolean\n isMfaEnabled: boolean\n loadMfa: () => void\n loadingError?: Error\n}\n\nfunction MfaStatus({\n isExternalIdentityProviderUser,\n isLoading,\n loadingError,\n loadMfa,\n isMfaEnabled,\n}: MfaStatusProps) {\n if (isExternalIdentityProviderUser) return null\n\n if (isLoading) {\n return (\n <Box padding={3}>\n <Grid container justifyContent=\"center\">\n <CircularProgress />\n </Grid>\n </Box>\n )\n }\n\n if (loadingError) {\n return (\n <div>\n <ErrorMessage\n title=\"Error Loading Multi Factor Authentication Configuration\"\n onTryAgain={loadMfa}\n >\n {loadingError.message}\n </ErrorMessage>\n </div>\n )\n }\n\n if (isMfaEnabled) {\n return (\n <Chip\n label=\"Enabled\"\n icon={<MaterialIcon color=\"success\">verified_user</MaterialIcon>}\n />\n )\n }\n\n return (\n <Chip\n label=\"Disabled\"\n icon={<MaterialIcon color=\"warning\">remove_moderator</MaterialIcon>}\n />\n )\n}\n\nfunction MfaSetup({ ssoSetupUrl }: { ssoSetupUrl: string }) {\n const {\n setupError,\n isMfaEnabled,\n isDisablingMfa,\n isSettingUpMfa,\n mfaSetup,\n beginMfaSetup,\n cancelMfaSetup,\n completeMfaSetup,\n clearMfaSetupError,\n beginDisablingMfa,\n completeDisablingMfa,\n cancelDisablingMfa,\n isExternalIdentityProviderUser,\n } = useMfa()\n\n if (ssoSetupUrl) {\n return (\n <Grid>\n <Button\n variant=\"outlined\"\n size=\"small\"\n component=\"a\"\n href={ssoSetupUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-cypress=\"configure-mfa-button\"\n >\n Configure MFA\n </Button>\n </Grid>\n )\n }\n\n if (isExternalIdentityProviderUser) {\n return (\n <Grid>\n <Tooltip title=\"MFA must be configured in your login provider.\">\n <span>\n <Button\n variant=\"outlined\"\n size=\"small\"\n disabled={isExternalIdentityProviderUser}\n data-cypress=\"configure-mfa-button\"\n >\n Configure MFA\n </Button>\n </span>\n </Tooltip>\n </Grid>\n )\n }\n\n return (\n <>\n <Grid>\n <Button\n variant=\"outlined\"\n size=\"small\"\n disabled={!isMfaEnabled}\n data-cypress=\"disable-mfa-button\"\n onClick={beginDisablingMfa}\n >\n Disable MFA\n </Button>\n <ConfirmDialog\n isOpen={isDisablingMfa}\n onClose={cancelDisablingMfa}\n onConfirm={completeDisablingMfa}\n title=\"Please Confirm\"\n confirmButtonText=\"Disable MFA\"\n confirmButtonIcon={<MaterialIcon>remove_moderator</MaterialIcon>}\n cypress={{\n dialog: 'disable-mfa-dialog',\n confirmButton: 'disable-mfa-dialog-confirm-button',\n cancelButton: 'disable-mfa-dialog-cancel-button',\n error: 'disable-mfa-dialog-error-message',\n }}\n >\n <Typography variant=\"body2\">\n Are you sure want to disable multi factor authentication (MFA)?\n </Typography>\n </ConfirmDialog>\n </Grid>\n\n <Grid>\n <Button\n variant=\"contained\"\n size=\"small\"\n loading={isSettingUpMfa}\n disabled={isMfaEnabled}\n onClick={beginMfaSetup}\n data-cypress=\"setup-mfa-button\"\n >\n Setup MFA\n </Button>\n <MfaDialog\n mfaSetup={mfaSetup}\n onClose={cancelMfaSetup}\n onCompleted={completeMfaSetup}\n />\n </Grid>\n <ErrorSnackbar open={!!setupError} onClose={clearMfaSetupError}>\n <span data-cypress=\"mfa-setup-error-message\">\n {setupError?.message}\n </span>\n </ErrorSnackbar>\n </>\n )\n}\n/**\n * React Component that provides a mechanism for app users to configure Multi\n * Factor Authentication. `<MfaProvider />` must be provided above this\n * component in the component tree.\n *\n * #### Example\n *\n * ```js\n * import * as React from 'react'\n * import {\n * MfaProvider,\n * MultiFactorAuthentication,\n * } from '@oneblink/apps-react'\n *\n * function Component() {\n * return <MultiFactorAuthentication />\n * }\n *\n * function App() {\n * return (\n * <MfaProvider>\n * <Component />\n * </MfaProvider>\n * )\n * }\n *\n * const root = document.getElementById('root')\n * if (root) {\n * ReactDOM.render(<App />, root)\n * }\n * ```\n *\n * @param props\n * @returns\n * @group Components\n */\nexport default function MultiFactorAuthentication({ ssoSetupUrl }: Props) {\n const {\n loadingError,\n isLoading,\n isMfaEnabled,\n loadMfa,\n isExternalIdentityProviderUser,\n } = useMfa()\n\n return (\n <Grid size={{ xs: 'grow', lg: 8 }}>\n <Box padding={3}>\n <Paper>\n <Box padding={3}>\n <Grid container spacing={2} alignItems=\"center\">\n <Grid size={{ xs: 'grow' }}>\n <Typography variant=\"h4\" fontWeight=\"light\">\n Multi Factor Authentication{' '}\n <MfaStatus\n loadMfa={loadMfa}\n isLoading={isLoading}\n loadingError={loadingError}\n isMfaEnabled={isMfaEnabled}\n isExternalIdentityProviderUser={\n !!isExternalIdentityProviderUser\n }\n />\n </Typography>\n <Box marginY={1}>\n <Divider />\n </Box>\n <Typography variant=\"body2\" paragraph>\n Multi factor authentication (MFA), also known as two factor\n authentication (2FA), is a best practice that requires a\n second authentication factor in addition to user name and\n password sign-in credentials. We strongly recommend enabling\n MFA to enhance your account security.\n </Typography>\n <Grid container justifyContent=\"flex-end\" spacing={1}>\n <MfaSetup ssoSetupUrl={ssoSetupUrl || ''} />\n </Grid>\n </Grid>\n </Grid>\n </Box>\n </Paper>\n </Box>\n </Grid>\n )\n}\n"]}
1
+ {"version":3,"file":"MultiFactorAuthentication.js","sourceRoot":"","sources":["../../../src/components/mfa/MultiFactorAuthentication.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react'\nimport {\n Box,\n Button,\n Divider,\n Grid,\n Paper,\n Tooltip,\n Typography,\n} from '@mui/material'\nimport { MiscTypes } from '@oneblink/types'\nimport MfaAuthenticatorAppDialog from './MfaAuthenticatorAppDialog'\nimport useMfa from '../../hooks/useMfa'\nimport MfaDisableDialog from './MfaDisableDialog'\nimport MfaRemovePhoneNumberDialog from './MfaRemovePhoneNumberDialog'\nimport MfaPhoneNumberDialog from './MfaPhoneNumberDialog'\nimport MfaMethodRow from './MfaMethodRow'\nimport MfaSuccessSnackbar from './MfaSuccessSnackbar'\nimport MfaErrorSnackbar from './MfaErrorSnackbar'\nimport MfaStatusChip from './MfaStatusChip'\nimport { formatMfaMethodNotAcceptedMessage } from '../../utils/mfa-requirement'\n\ntype Props = {\n mfaRequirement: MiscTypes.MfaRequirement | undefined\n ssoSetupUrl?: string\n}\n\nfunction MfaMethodList({ mfaRequirement }: Pick<Props, 'mfaRequirement'>) {\n const {\n mfaSettings,\n loadingError,\n isLoading,\n isSettingUpMfa,\n settingUpMfaMethod,\n isSettingPreferredMfaMethod,\n beginMfaSetup,\n beginDisablingMfaMethod,\n setPreferredMfaMethod,\n beginRemovingPhoneNumber,\n } = useMfa()\n\n const phoneDetail = useMemo(() => {\n if (!mfaSettings.sms.phoneNumber) {\n return undefined\n }\n\n if (!mfaSettings.sms.isPhoneNumberVerified) {\n return `Phone number: ${mfaSettings.sms.phoneNumber} (not verified)`\n }\n\n return `Phone number: ${mfaSettings.sms.phoneNumber}`\n }, [mfaSettings])\n\n const authenticatorMfaRequirementMessage = useMemo(() => {\n return formatMfaMethodNotAcceptedMessage('authenticatorApp', mfaRequirement)\n }, [mfaRequirement])\n\n const smsMfaRequirementMessage = useMemo(() => {\n return formatMfaMethodNotAcceptedMessage('sms', mfaRequirement)\n }, [mfaRequirement])\n\n if (isLoading) {\n return (\n <Typography variant=\"body2\" color=\"text.secondary\">\n Loading MFA methods...\n </Typography>\n )\n }\n\n if (loadingError) {\n return (\n <Typography variant=\"body2\" color=\"error\">\n We are unable to load your MFA methods. Please try again by clicking the\n reload button on the chip above.\n </Typography>\n )\n }\n\n return (\n <>\n <MfaMethodRow\n isEnabled={mfaSettings.authenticator.enabled}\n isPreferred={mfaSettings.authenticator.preferred}\n isSettingUp={isSettingUpMfa && settingUpMfaMethod === 'authenticator'}\n isSettingPreferredMfaMethod={isSettingPreferredMfaMethod}\n isSetupDisabled={!!loadingError || isSettingUpMfa}\n showSetupErrorTooltip={!!loadingError}\n title=\"Authenticator App\"\n description=\"Use an app like Google Authenticator or Microsoft Authenticator to generate 6-digit verification codes.\"\n mfaRequirementMessage={authenticatorMfaRequirementMessage}\n cypressPrefix=\"mfa-authenticator\"\n onSetup={() => beginMfaSetup('authenticator')}\n onDisable={() => beginDisablingMfaMethod('authenticator')}\n onSetPreferred={() => setPreferredMfaMethod('authenticator')}\n />\n <Divider sx={{ my: 2 }} />\n <MfaMethodRow\n isEnabled={mfaSettings.sms.enabled}\n isPreferred={mfaSettings.sms.preferred}\n isSettingUp={isSettingUpMfa && settingUpMfaMethod === 'sms'}\n isSettingPreferredMfaMethod={isSettingPreferredMfaMethod}\n isSetupDisabled={!!loadingError || isSettingUpMfa}\n showSetupErrorTooltip={!!loadingError}\n title=\"SMS\"\n description=\"Receive a one-time verification code via SMS each time MFA is required.\"\n detail={phoneDetail}\n mfaRequirementMessage={smsMfaRequirementMessage}\n cypressPrefix=\"mfa-sms\"\n onSetup={() => beginMfaSetup('sms')}\n onDisable={() => beginDisablingMfaMethod('sms')}\n onSetPreferred={() => setPreferredMfaMethod('sms')}\n extraButtons={\n !!mfaSettings.sms.phoneNumber && !mfaSettings.sms.enabled ? (\n <Button\n size=\"small\"\n variant=\"outlined\"\n disabled={!!loadingError}\n onClick={beginRemovingPhoneNumber}\n data-cypress=\"mfa-sms-remove-phone-button\"\n >\n Remove Phone\n </Button>\n ) : undefined\n }\n />\n </>\n )\n}\n\nfunction MfaSetup({ ssoSetupUrl, mfaRequirement }: Props) {\n const { isExternalIdentityProviderUser } = useMfa()\n\n if (ssoSetupUrl) {\n return (\n <Button\n variant=\"outlined\"\n size=\"small\"\n component=\"a\"\n href={ssoSetupUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-cypress=\"configure-mfa-button\"\n >\n Configure MFA\n </Button>\n )\n }\n\n if (isExternalIdentityProviderUser) {\n return (\n <Tooltip title=\"MFA must be configured in your login provider.\">\n <span>\n <Button\n variant=\"outlined\"\n disabled={isExternalIdentityProviderUser}\n data-cypress=\"configure-mfa-button\"\n >\n Configure MFA\n </Button>\n </span>\n </Tooltip>\n )\n }\n\n return (\n <>\n <MfaMethodList mfaRequirement={mfaRequirement} />\n <MfaDisableDialog />\n <MfaRemovePhoneNumberDialog />\n <MfaPhoneNumberDialog />\n <MfaAuthenticatorAppDialog />\n <MfaErrorSnackbar />\n <MfaSuccessSnackbar />\n </>\n )\n}\n\n/**\n * React Component that provides a mechanism for app users to configure Multi\n * Factor Authentication. `<MfaProvider />` must be provided above this\n * component in the component tree.\n *\n * #### Example\n *\n * ```js\n * import * as React from 'react'\n * import {\n * MfaProvider,\n * MultiFactorAuthentication,\n * } from '@oneblink/apps-react'\n *\n * function Component() {\n * return <MultiFactorAuthentication mfaRequirement={undefined} />\n * }\n *\n * function AppWithMfaRequirement({ mfaRequirement }) {\n * return (\n * <MfaProvider>\n * <MultiFactorAuthentication mfaRequirement={mfaRequirement} />\n * </MfaProvider>\n * )\n * }\n * ```\n *\n * @param props\n * @param props.mfaRequirement - The MFA methods allowed by your administrator\n * for using this app. Pass `undefined` when the app has no MFA requirement.\n * Users can still enable other methods, but will be warned when their\n * configuration does not meet this requirement.\n * @returns\n * @group Components\n */\nexport default function MultiFactorAuthentication({\n mfaRequirement,\n ssoSetupUrl,\n}: Props) {\n return (\n <Grid size={{ xs: 'grow', lg: 8 }}>\n <Box padding={3}>\n <Paper>\n <Box padding={3}>\n <Grid container spacing={2} alignItems=\"center\">\n <Grid size={{ xs: 'grow' }}>\n <Typography variant=\"h4\" fontWeight=\"light\">\n Multi Factor Authentication <MfaStatusChip />\n </Typography>\n <Box marginY={1}>\n <Divider />\n </Box>\n <Typography variant=\"body2\" paragraph>\n Multi factor authentication (MFA), also known as two factor\n authentication (2FA), is a best practice that requires a\n second authentication factor in addition to user name and\n password sign-in credentials. We strongly recommend enabling\n MFA to enhance your account security.\n </Typography>\n <MfaSetup\n ssoSetupUrl={ssoSetupUrl}\n mfaRequirement={mfaRequirement}\n />\n </Grid>\n </Grid>\n </Box>\n </Paper>\n </Box>\n </Grid>\n )\n}\n"]}