@burdenoff/microfe-auth 2026.925.1 → 2026.1009.1
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/README.md +14 -0
- package/dist/auth/AuthRoot.d.ts +0 -1
- package/dist/auth/AuthRoot.js.map +1 -1
- package/dist/auth/AuthRoutes.d.ts +1 -1
- package/dist/auth/AuthRoutes.js.map +1 -1
- package/dist/auth/components/AuthIcons.d.ts +12 -13
- package/dist/auth/components/AuthIcons.js.map +1 -1
- package/dist/auth/components/AuthLayout.d.ts +1 -2
- package/dist/auth/components/AuthLayout.js.map +1 -1
- package/dist/auth/components/CaptchaV2.d.ts +1 -1
- package/dist/auth/components/CaptchaV2.js +2 -2
- package/dist/auth/components/CaptchaV2.js.map +1 -1
- package/dist/auth/components/ConfirmActionDialog.d.ts +1 -1
- package/dist/auth/components/ConfirmActionDialog.js.map +1 -1
- package/dist/auth/components/profile/UserProfileMenu.d.ts +1 -1
- package/dist/auth/components/profile/UserProfileMenu.js.map +1 -1
- package/dist/auth/components/workspace/WorkspaceSelector.d.ts +1 -2
- package/dist/auth/components/workspace/WorkspaceSelector.js.map +1 -1
- package/dist/auth/constants/permissions.js.map +1 -1
- package/dist/auth/hooks/useAccountOperations.d.ts +0 -1
- package/dist/auth/hooks/useAccountOperations.js.map +1 -1
- package/dist/auth/hooks/useActiveContextBootstrapMirror.js.map +1 -1
- package/dist/auth/hooks/useApiKeyOperations.d.ts +1 -2
- package/dist/auth/hooks/useAppOperations.d.ts +1 -2
- package/dist/auth/hooks/useAuthOperations.d.ts +0 -1
- package/dist/auth/hooks/useAuthOperations.js.map +1 -1
- package/dist/auth/hooks/useAuthPermissions.js.map +1 -1
- package/dist/auth/hooks/useCaptchaV3.js +1 -1
- package/dist/auth/hooks/useCaptchaV3.js.map +1 -1
- package/dist/auth/hooks/useContextSwitcherData.d.ts +1 -1
- package/dist/auth/hooks/useContextSwitcherData.js +346 -259
- package/dist/auth/hooks/useContextSwitcherData.js.map +1 -1
- package/dist/auth/hooks/useDelegationOperations.d.ts +0 -1
- package/dist/auth/hooks/useMfaOperations.d.ts +0 -1
- package/dist/auth/hooks/useMfaOperations.js.map +1 -1
- package/dist/auth/hooks/useMyWorkspaces.d.ts +0 -1
- package/dist/auth/hooks/useMyWorkspaces.js.map +1 -1
- package/dist/auth/hooks/useOAuthOperations.d.ts +0 -1
- package/dist/auth/hooks/useOAuthOperations.js +2 -2
- package/dist/auth/hooks/useOAuthOperations.js.map +1 -1
- package/dist/auth/hooks/usePasswordOperations.d.ts +0 -1
- package/dist/auth/hooks/usePasswordOperations.js.map +1 -1
- package/dist/auth/hooks/useSessionOperations.d.ts +0 -1
- package/dist/auth/hooks/useSessionOperations.js.map +1 -1
- package/dist/auth/hooks/useSignupCaptcha.js +1 -1
- package/dist/auth/hooks/useSignupCaptcha.js.map +1 -1
- package/dist/auth/hooks/useSsoOperations.d.ts +0 -1
- package/dist/auth/hooks/useSsoOperations.js.map +1 -1
- package/dist/auth/hooks/useTokenRefresh.js.map +1 -1
- package/dist/auth/hooks/useUserOperations.d.ts +0 -1
- package/dist/auth/hooks/useUserOperations.js.map +1 -1
- package/dist/auth/hooks/useWorkspaceOperations.d.ts +0 -1
- package/dist/auth/hooks/useWorkspaceOperations.js.map +1 -1
- package/dist/auth/hooks/useWorkspaceToken.js.map +1 -1
- package/dist/auth/pages/AuditLogPage.d.ts +1 -1
- package/dist/auth/pages/AuditLogPage.js.map +1 -1
- package/dist/auth/pages/EmailVerifyPage.d.ts +1 -1
- package/dist/auth/pages/EmailVerifyPage.js.map +1 -1
- package/dist/auth/pages/LoginPage.d.ts +1 -1
- package/dist/auth/pages/LoginPage.js.map +1 -1
- package/dist/auth/pages/MagicLinkPage.d.ts +1 -1
- package/dist/auth/pages/MagicLinkPage.js.map +1 -1
- package/dist/auth/pages/MfaSetupPage.d.ts +1 -1
- package/dist/auth/pages/MfaSetupPage.js +88 -88
- package/dist/auth/pages/MfaSetupPage.js.map +1 -1
- package/dist/auth/pages/MfaVerifyPage.d.ts +1 -1
- package/dist/auth/pages/MfaVerifyPage.js.map +1 -1
- package/dist/auth/pages/OAuthCallbackPage.d.ts +1 -1
- package/dist/auth/pages/OAuthCallbackPage.js.map +1 -1
- package/dist/auth/pages/PasswordResetPage.d.ts +1 -1
- package/dist/auth/pages/PasswordResetPage.js.map +1 -1
- package/dist/auth/pages/ReactivatePage.d.ts +1 -1
- package/dist/auth/pages/ReactivatePage.js.map +1 -1
- package/dist/auth/pages/RegisterPage.d.ts +1 -1
- package/dist/auth/pages/RegisterPage.js.map +1 -1
- package/dist/auth/pages/SSOClearPage.d.ts +1 -1
- package/dist/auth/pages/SSOClearPage.js.map +1 -1
- package/dist/auth/pages/SSODepositPage.d.ts +1 -1
- package/dist/auth/pages/SSODepositPage.js.map +1 -1
- package/dist/auth/pages/SSORelayCallbackPage.d.ts +1 -1
- package/dist/auth/pages/SSORelayCallbackPage.js.map +1 -1
- package/dist/auth/pages/SSORelayPage.d.ts +1 -1
- package/dist/auth/pages/SSORelayPage.js.map +1 -1
- package/dist/auth/pages/SecurityPage.d.ts +1 -1
- package/dist/auth/pages/SecurityPage.js.map +1 -1
- package/dist/auth/pages/SessionBridgePage.d.ts +1 -1
- package/dist/auth/pages/SessionBridgePage.js.map +1 -1
- package/dist/auth/pages/SessionsPage.d.ts +1 -1
- package/dist/auth/pages/SessionsPage.js.map +1 -1
- package/dist/auth/pages/SsoCallbackPage.d.ts +1 -1
- package/dist/auth/pages/SsoCallbackPage.js.map +1 -1
- package/dist/auth/pages/SsoConfigPage.d.ts +1 -1
- package/dist/auth/pages/SsoConfigPage.js.map +1 -1
- package/dist/auth/pages/SsoLoginPage.d.ts +1 -1
- package/dist/auth/pages/SsoLoginPage.js.map +1 -1
- package/dist/auth/pages/UserDetailsPage.d.ts +1 -1
- package/dist/auth/pages/UserDetailsPage.js.map +1 -1
- package/dist/auth/pages/UserProfilePage.d.ts +1 -1
- package/dist/auth/pages/UserProfilePage.js.map +1 -1
- package/dist/auth/pages/UsersPage.d.ts +1 -1
- package/dist/auth/pages/UsersPage.js.map +1 -1
- package/dist/auth/providers/AuthProvider.d.ts +0 -1
- package/dist/auth/providers/AuthProvider.js.map +1 -1
- package/dist/auth/providers/CaptchaProvider.d.ts +1 -2
- package/dist/auth/providers/CaptchaProvider.js +2 -2
- package/dist/auth/providers/CaptchaProvider.js.map +1 -1
- package/dist/auth/store/authStore.d.ts +0 -1
- package/dist/auth/store/authStore.js.map +1 -1
- package/dist/auth/types/index.d.ts +0 -1
- package/dist/auth/utils/captchaConfig.js.map +1 -1
- package/dist/auth/utils/formatters.js.map +1 -1
- package/dist/auth/utils/gatewayBaseUrls.js.map +1 -1
- package/dist/auth/utils/legalUrls.js.map +1 -1
- package/dist/auth/utils/redirect.js.map +1 -1
- package/dist/auth/utils/ssoOrigins.js.map +1 -1
- package/dist/auth/utils/validation.d.ts +0 -1
- package/dist/auth/utils/validation.js.map +1 -1
- package/dist/dev/main.d.ts +0 -1
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/shared/hooks/useTr.js.map +1 -1
- package/dist/shared/types/index.d.ts +0 -1
- package/dist/translations/en.js.map +1 -1
- package/package.json +27 -20
- package/dist/_virtual/_rolldown/runtime.js +0 -13
- package/dist/node_modules/hoist-non-react-statics/dist/hoist-non-react-statics.cjs.js +0 -70
- package/dist/node_modules/hoist-non-react-statics/dist/hoist-non-react-statics.cjs.js.map +0 -1
- package/dist/node_modules/object-assign/index.js +0 -41
- package/dist/node_modules/object-assign/index.js.map +0 -1
- package/dist/node_modules/prop-types/checkPropTypes.js +0 -46
- package/dist/node_modules/prop-types/checkPropTypes.js.map +0 -1
- package/dist/node_modules/prop-types/factoryWithThrowingShims.js +0 -49
- package/dist/node_modules/prop-types/factoryWithThrowingShims.js.map +0 -1
- package/dist/node_modules/prop-types/factoryWithTypeCheckers.js +0 -272
- package/dist/node_modules/prop-types/factoryWithTypeCheckers.js.map +0 -1
- package/dist/node_modules/prop-types/index.js +0 -16
- package/dist/node_modules/prop-types/index.js.map +0 -1
- package/dist/node_modules/prop-types/lib/ReactPropTypesSecret.js +0 -10
- package/dist/node_modules/prop-types/lib/ReactPropTypesSecret.js.map +0 -1
- package/dist/node_modules/prop-types/lib/has.js +0 -10
- package/dist/node_modules/prop-types/lib/has.js.map +0 -1
- package/dist/node_modules/qrcode.react/lib/esm/index.js +0 -959
- package/dist/node_modules/qrcode.react/lib/esm/index.js.map +0 -1
- package/dist/node_modules/react-async-script/lib/esm/async-script-loader.js +0 -114
- package/dist/node_modules/react-async-script/lib/esm/async-script-loader.js.map +0 -1
- package/dist/node_modules/react-google-recaptcha/lib/esm/index.js +0 -8
- package/dist/node_modules/react-google-recaptcha/lib/esm/index.js.map +0 -1
- package/dist/node_modules/react-google-recaptcha/lib/esm/recaptcha-wrapper.js +0 -20
- package/dist/node_modules/react-google-recaptcha/lib/esm/recaptcha-wrapper.js.map +0 -1
- package/dist/node_modules/react-google-recaptcha/lib/esm/recaptcha.js +0 -148
- package/dist/node_modules/react-google-recaptcha/lib/esm/recaptcha.js.map +0 -1
- package/dist/node_modules/react-google-recaptcha-v3/dist/react-google-recaptcha-v3.esm.js +0 -445
- package/dist/node_modules/react-google-recaptcha-v3/dist/react-google-recaptcha-v3.esm.js.map +0 -1
- package/dist/node_modules/react-is/cjs/react-is.development.js +0 -84
- package/dist/node_modules/react-is/cjs/react-is.development.js.map +0 -1
- package/dist/node_modules/react-is/cjs/react-is.production.min.js +0 -64
- package/dist/node_modules/react-is/cjs/react-is.production.min.js.map +0 -1
- package/dist/node_modules/react-is/index.js +0 -12
- package/dist/node_modules/react-is/index.js.map +0 -1
|
@@ -3,26 +3,26 @@ import { useTr as t } from "../../shared/hooks/useTr.js";
|
|
|
3
3
|
import { AlertIcon as n, SpinnerIcon as r } from "../components/AuthIcons.js";
|
|
4
4
|
import { useMfaOperations as i } from "../hooks/useMfaOperations.js";
|
|
5
5
|
import { ConfirmActionDialog as a } from "../components/ConfirmActionDialog.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
6
|
+
import { nativeCopyText as o, nativeImpact as s, nativeNotify as c } from "../../utils/nativeBridge.js";
|
|
7
|
+
import { useState as l } from "react";
|
|
8
|
+
import { useNavigate as u } from "react-router";
|
|
9
|
+
import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
10
|
+
import { Button as m } from "@burdenoff/fe-libs/button";
|
|
11
|
+
import { NextSteps as h, PagePurpose as g } from "@burdenoff/fe-libs/ui";
|
|
12
|
+
import { QRCodeSVG as _ } from "qrcode.react";
|
|
13
13
|
//#region src/auth/pages/MfaSetupPage.tsx
|
|
14
14
|
function v() {
|
|
15
|
-
let v = t(), y =
|
|
15
|
+
let v = t(), y = u(), { setupMFA: b, enableMFA: x, disableMFA: S, isLoading: C } = i(), w = e((e) => e.user), T = e((e) => e.isAuthenticated), [E, D] = l(null), [O, k] = l(""), [A, j] = l(null), [M, N] = l(null), [P, F] = l(!1), [I, L] = l(!1), R = async (e) => {
|
|
16
16
|
try {
|
|
17
17
|
j(null), N(null), D(await b(e));
|
|
18
18
|
} catch (e) {
|
|
19
19
|
console.error("Failed to setup MFA:", e), j(e instanceof Error ? e.message : "Failed to setup MFA. Please try again.");
|
|
20
20
|
}
|
|
21
21
|
};
|
|
22
|
-
return T ? /* @__PURE__ */
|
|
22
|
+
return T ? /* @__PURE__ */ p("div", {
|
|
23
23
|
className: "max-w-2xl mx-auto p-6",
|
|
24
24
|
children: [
|
|
25
|
-
/* @__PURE__ */
|
|
25
|
+
/* @__PURE__ */ f(a, {
|
|
26
26
|
open: I,
|
|
27
27
|
onOpenChange: L,
|
|
28
28
|
title: v("auth.mfaSetup.disableTitle", "Disable two-factor authentication?"),
|
|
@@ -38,52 +38,52 @@ function v() {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
}),
|
|
41
|
-
/* @__PURE__ */
|
|
41
|
+
/* @__PURE__ */ p("div", {
|
|
42
42
|
className: "mb-8",
|
|
43
43
|
children: [
|
|
44
|
-
/* @__PURE__ */
|
|
44
|
+
/* @__PURE__ */ f("h1", {
|
|
45
45
|
className: "text-3xl font-semibold text-[var(--color-text-primary)]",
|
|
46
46
|
children: v("auth.mfaSetup.title", "Two-Factor Authentication")
|
|
47
47
|
}),
|
|
48
|
-
/* @__PURE__ */
|
|
48
|
+
/* @__PURE__ */ f("p", {
|
|
49
49
|
className: "text-[var(--color-text-secondary)] mt-2",
|
|
50
50
|
children: v("auth.mfaSetup.subtitle", "Add an extra layer of security to your account")
|
|
51
51
|
}),
|
|
52
|
-
/* @__PURE__ */
|
|
52
|
+
/* @__PURE__ */ f(g, {
|
|
53
53
|
className: "mt-3",
|
|
54
54
|
children: v("auth.mfaSetup.purpose", "Two-factor authentication (2FA) asks for a one-time code from an authenticator app at sign-in, so your account stays safe even if your password leaks. Set it up here and keep your backup codes somewhere safe.")
|
|
55
55
|
})
|
|
56
56
|
]
|
|
57
57
|
}),
|
|
58
|
-
M && /* @__PURE__ */
|
|
58
|
+
M && /* @__PURE__ */ f("div", {
|
|
59
59
|
className: "mb-6 p-4 rounded-xl bg-[var(--color-status-success-bg-subtle)] border border-[var(--color-status-success-border)]",
|
|
60
|
-
children: /* @__PURE__ */
|
|
60
|
+
children: /* @__PURE__ */ p("div", {
|
|
61
61
|
className: "flex items-center gap-3",
|
|
62
|
-
children: [/* @__PURE__ */
|
|
62
|
+
children: [/* @__PURE__ */ f("svg", {
|
|
63
63
|
className: "size-5 text-[var(--color-status-success-text)]",
|
|
64
64
|
fill: "none",
|
|
65
65
|
viewBox: "0 0 24 24",
|
|
66
66
|
stroke: "currentColor",
|
|
67
|
-
children: /* @__PURE__ */
|
|
67
|
+
children: /* @__PURE__ */ f("path", {
|
|
68
68
|
strokeLinecap: "round",
|
|
69
69
|
strokeLinejoin: "round",
|
|
70
70
|
strokeWidth: 2,
|
|
71
71
|
d: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
72
72
|
})
|
|
73
|
-
}), /* @__PURE__ */
|
|
73
|
+
}), /* @__PURE__ */ f("p", {
|
|
74
74
|
className: "text-[var(--color-status-success-text)]",
|
|
75
75
|
children: M
|
|
76
76
|
})]
|
|
77
77
|
})
|
|
78
78
|
}),
|
|
79
|
-
A && /* @__PURE__ */
|
|
79
|
+
A && /* @__PURE__ */ p("div", {
|
|
80
80
|
className: "mb-6 flex items-start gap-3 p-4 rounded-xl bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)]",
|
|
81
|
-
children: [/* @__PURE__ */
|
|
81
|
+
children: [/* @__PURE__ */ f(n, { className: "size-5 text-[var(--color-status-error-text)] flex-shrink-0 mt-0.5" }), /* @__PURE__ */ p("div", {
|
|
82
82
|
className: "flex-1",
|
|
83
|
-
children: [/* @__PURE__ */
|
|
83
|
+
children: [/* @__PURE__ */ f("p", {
|
|
84
84
|
className: "text-sm text-[var(--color-status-error-text)]",
|
|
85
85
|
children: A
|
|
86
|
-
}), /* @__PURE__ */
|
|
86
|
+
}), /* @__PURE__ */ f("button", {
|
|
87
87
|
type: "button",
|
|
88
88
|
onClick: () => j(null),
|
|
89
89
|
className: "mt-2 text-sm text-[var(--color-status-error-text)] hover:underline transition-colors duration-200",
|
|
@@ -91,39 +91,39 @@ function v() {
|
|
|
91
91
|
})]
|
|
92
92
|
})]
|
|
93
93
|
}),
|
|
94
|
-
E ? /* @__PURE__ */
|
|
94
|
+
E ? /* @__PURE__ */ p("div", {
|
|
95
95
|
className: "p-6 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-sm",
|
|
96
|
-
children: [/* @__PURE__ */
|
|
96
|
+
children: [/* @__PURE__ */ p("div", {
|
|
97
97
|
className: "flex items-center gap-3 mb-6",
|
|
98
|
-
children: [/* @__PURE__ */
|
|
98
|
+
children: [/* @__PURE__ */ f("div", {
|
|
99
99
|
className: "size-10 rounded-full bg-[var(--color-bg-accent)] flex items-center justify-center",
|
|
100
|
-
children: /* @__PURE__ */
|
|
100
|
+
children: /* @__PURE__ */ f("svg", {
|
|
101
101
|
className: "size-5 text-[var(--color-text-link)]",
|
|
102
102
|
fill: "none",
|
|
103
103
|
viewBox: "0 0 24 24",
|
|
104
104
|
stroke: "currentColor",
|
|
105
|
-
children: /* @__PURE__ */
|
|
105
|
+
children: /* @__PURE__ */ f("path", {
|
|
106
106
|
strokeLinecap: "round",
|
|
107
107
|
strokeLinejoin: "round",
|
|
108
108
|
strokeWidth: 2,
|
|
109
109
|
d: "M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
|
|
110
110
|
})
|
|
111
111
|
})
|
|
112
|
-
}), /* @__PURE__ */
|
|
112
|
+
}), /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("h2", {
|
|
113
113
|
className: "text-xl font-semibold text-[var(--color-text-primary)]",
|
|
114
114
|
children: v("auth.mfaSetup.setupAuthApp", "Setup Authenticator App")
|
|
115
|
-
}), /* @__PURE__ */
|
|
115
|
+
}), /* @__PURE__ */ f("p", {
|
|
116
116
|
className: "text-sm text-[var(--color-text-secondary)]",
|
|
117
117
|
children: v("auth.mfaSetup.scanQr", "Scan the QR code with your authenticator app")
|
|
118
118
|
})] })]
|
|
119
|
-
}), /* @__PURE__ */
|
|
119
|
+
}), /* @__PURE__ */ p("div", {
|
|
120
120
|
className: "space-y-6",
|
|
121
121
|
children: [
|
|
122
|
-
E.qrCode && /* @__PURE__ */
|
|
122
|
+
E.qrCode && /* @__PURE__ */ f("div", {
|
|
123
123
|
className: "flex justify-center",
|
|
124
|
-
children: /* @__PURE__ */
|
|
124
|
+
children: /* @__PURE__ */ f("div", {
|
|
125
125
|
className: "p-4 bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)]",
|
|
126
|
-
children: /* @__PURE__ */
|
|
126
|
+
children: /* @__PURE__ */ f(_, {
|
|
127
127
|
value: E.qrCode,
|
|
128
128
|
size: 192,
|
|
129
129
|
level: "M",
|
|
@@ -132,40 +132,40 @@ function v() {
|
|
|
132
132
|
})
|
|
133
133
|
})
|
|
134
134
|
}),
|
|
135
|
-
E.secret && /* @__PURE__ */
|
|
135
|
+
E.secret && /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("p", {
|
|
136
136
|
className: "text-sm text-[var(--color-text-secondary)] mb-2",
|
|
137
137
|
children: v("auth.mfaSetup.cantScan", "Can't scan the QR code? Enter this key manually:")
|
|
138
|
-
}), /* @__PURE__ */
|
|
138
|
+
}), /* @__PURE__ */ f("code", {
|
|
139
139
|
className: "block p-3 rounded-lg bg-[var(--color-bg-muted)] text-[var(--color-text-primary)] font-mono text-sm break-all select-all",
|
|
140
140
|
children: E.secret
|
|
141
141
|
})] }),
|
|
142
|
-
E.backupCodes && E.backupCodes.length > 0 && /* @__PURE__ */
|
|
142
|
+
E.backupCodes && E.backupCodes.length > 0 && /* @__PURE__ */ p("div", {
|
|
143
143
|
className: "p-4 rounded-xl bg-[var(--color-status-warning-bg-subtle)] border border-[var(--color-status-warning-border)]",
|
|
144
144
|
children: [
|
|
145
|
-
/* @__PURE__ */
|
|
145
|
+
/* @__PURE__ */ p("div", {
|
|
146
146
|
className: "flex items-start gap-3 mb-3",
|
|
147
|
-
children: [/* @__PURE__ */
|
|
147
|
+
children: [/* @__PURE__ */ f("svg", {
|
|
148
148
|
className: "size-5 text-[var(--color-status-warning-text)] flex-shrink-0 mt-0.5",
|
|
149
149
|
fill: "none",
|
|
150
150
|
viewBox: "0 0 24 24",
|
|
151
151
|
stroke: "currentColor",
|
|
152
|
-
children: /* @__PURE__ */
|
|
152
|
+
children: /* @__PURE__ */ f("path", {
|
|
153
153
|
strokeLinecap: "round",
|
|
154
154
|
strokeLinejoin: "round",
|
|
155
155
|
strokeWidth: 2,
|
|
156
156
|
d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
|
157
157
|
})
|
|
158
|
-
}), /* @__PURE__ */
|
|
158
|
+
}), /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("h3", {
|
|
159
159
|
className: "font-medium text-[var(--color-status-warning-text)]",
|
|
160
160
|
children: v("auth.mfaSetup.saveBackupCodes", "Save your backup codes")
|
|
161
|
-
}), /* @__PURE__ */
|
|
161
|
+
}), /* @__PURE__ */ f("p", {
|
|
162
162
|
className: "text-sm text-[var(--color-status-warning-text)]",
|
|
163
163
|
children: v("auth.mfaSetup.storeCodesSafely", "Store these codes safely. You can use them to access your account if you lose your authenticator device.")
|
|
164
164
|
})] })]
|
|
165
165
|
}),
|
|
166
|
-
/* @__PURE__ */
|
|
166
|
+
/* @__PURE__ */ f("div", {
|
|
167
167
|
className: "p-3 rounded-lg bg-[var(--color-bg-surface)] grid grid-cols-2 gap-2 mb-3",
|
|
168
|
-
children: E.backupCodes.map((e, t) => /* @__PURE__ */
|
|
168
|
+
children: E.backupCodes.map((e, t) => /* @__PURE__ */ p("code", {
|
|
169
169
|
className: "text-sm text-[var(--color-text-primary)] font-mono",
|
|
170
170
|
children: [
|
|
171
171
|
t + 1,
|
|
@@ -174,21 +174,21 @@ function v() {
|
|
|
174
174
|
]
|
|
175
175
|
}, e))
|
|
176
176
|
}),
|
|
177
|
-
/* @__PURE__ */
|
|
177
|
+
/* @__PURE__ */ f("button", {
|
|
178
178
|
type: "button",
|
|
179
179
|
onClick: async () => {
|
|
180
|
-
E?.backupCodes && (
|
|
180
|
+
E?.backupCodes && (s("light"), await o(E.backupCodes.join("\n")) ? (F(!0), c("success"), setTimeout(() => F(!1), 2e3)) : c("error"));
|
|
181
181
|
},
|
|
182
182
|
className: "text-sm font-medium text-[var(--color-status-warning-text)] hover:underline transition-colors duration-200",
|
|
183
183
|
children: P ? v("auth.mfaSetup.copied", "Copied!") : v("auth.mfaSetup.copyBackupCodes", "Copy backup codes")
|
|
184
184
|
})
|
|
185
185
|
]
|
|
186
186
|
}),
|
|
187
|
-
/* @__PURE__ */
|
|
187
|
+
/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
|
|
188
188
|
htmlFor: "mfa-verification-code",
|
|
189
189
|
className: "block text-sm font-medium text-[var(--color-text-primary)] mb-2",
|
|
190
190
|
children: v("auth.mfaSetup.enterCode", "Enter the 6-digit code from your app")
|
|
191
|
-
}), /* @__PURE__ */
|
|
191
|
+
}), /* @__PURE__ */ f("input", {
|
|
192
192
|
id: "mfa-verification-code",
|
|
193
193
|
type: "text",
|
|
194
194
|
inputMode: "numeric",
|
|
@@ -201,16 +201,16 @@ function v() {
|
|
|
201
201
|
autoComplete: "one-time-code",
|
|
202
202
|
className: "block w-full px-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] text-center text-2xl tracking-widest font-mono focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all duration-300"
|
|
203
203
|
})] }),
|
|
204
|
-
/* @__PURE__ */
|
|
204
|
+
/* @__PURE__ */ p("div", {
|
|
205
205
|
className: "flex gap-3",
|
|
206
|
-
children: [/* @__PURE__ */
|
|
206
|
+
children: [/* @__PURE__ */ f("button", {
|
|
207
207
|
type: "button",
|
|
208
208
|
onClick: () => {
|
|
209
209
|
D(null), k(""), j(null);
|
|
210
210
|
},
|
|
211
211
|
className: "flex-1 py-3 px-4 rounded-xl border border-[var(--color-border-default)] text-[var(--color-text-secondary)] font-medium hover:bg-[var(--color-bg-muted)] transition-colors duration-200",
|
|
212
212
|
children: v("auth.mfaSetup.cancel", "Cancel")
|
|
213
|
-
}), /* @__PURE__ */
|
|
213
|
+
}), /* @__PURE__ */ f(m, {
|
|
214
214
|
type: "button",
|
|
215
215
|
onClick: async () => {
|
|
216
216
|
if (O.length !== 6) {
|
|
@@ -227,53 +227,53 @@ function v() {
|
|
|
227
227
|
variant: "default",
|
|
228
228
|
size: "lg",
|
|
229
229
|
className: "flex-1 font-semibold",
|
|
230
|
-
children: C ? /* @__PURE__ */
|
|
230
|
+
children: C ? /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f(r, { className: "size-5 animate-spin" }), /* @__PURE__ */ f("span", { children: v("auth.mfa.verifying", "Verifying...") })] }) : v("auth.mfaSetup.enable2fa", "Enable 2FA")
|
|
231
231
|
})]
|
|
232
232
|
})
|
|
233
233
|
]
|
|
234
234
|
})]
|
|
235
|
-
}) : /* @__PURE__ */
|
|
235
|
+
}) : /* @__PURE__ */ f("div", {
|
|
236
236
|
className: "p-6 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-sm",
|
|
237
|
-
children: w?.mfaEnabled ? /* @__PURE__ */
|
|
238
|
-
/* @__PURE__ */
|
|
237
|
+
children: w?.mfaEnabled ? /* @__PURE__ */ p(d, { children: [
|
|
238
|
+
/* @__PURE__ */ p("div", {
|
|
239
239
|
className: "flex items-center gap-4 mb-6",
|
|
240
|
-
children: [/* @__PURE__ */
|
|
240
|
+
children: [/* @__PURE__ */ f("div", {
|
|
241
241
|
className: "size-12 rounded-full bg-[var(--color-status-success-bg-subtle)] flex items-center justify-center",
|
|
242
|
-
children: /* @__PURE__ */
|
|
242
|
+
children: /* @__PURE__ */ f("svg", {
|
|
243
243
|
className: "size-6 text-[var(--color-status-success-text)]",
|
|
244
244
|
fill: "none",
|
|
245
245
|
viewBox: "0 0 24 24",
|
|
246
246
|
stroke: "currentColor",
|
|
247
|
-
children: /* @__PURE__ */
|
|
247
|
+
children: /* @__PURE__ */ f("path", {
|
|
248
248
|
strokeLinecap: "round",
|
|
249
249
|
strokeLinejoin: "round",
|
|
250
250
|
strokeWidth: 2,
|
|
251
251
|
d: "M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"
|
|
252
252
|
})
|
|
253
253
|
})
|
|
254
|
-
}), /* @__PURE__ */
|
|
254
|
+
}), /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("h2", {
|
|
255
255
|
className: "text-xl font-semibold text-[var(--color-text-primary)]",
|
|
256
256
|
children: v("auth.mfaSetup.twoFactorOn", "Two-Factor Authentication is On")
|
|
257
|
-
}), /* @__PURE__ */
|
|
257
|
+
}), /* @__PURE__ */ f("p", {
|
|
258
258
|
className: "text-[var(--color-text-secondary)]",
|
|
259
259
|
children: v("auth.mfaSetup.protected", "Your account is protected with an authenticator app")
|
|
260
260
|
})] })]
|
|
261
261
|
}),
|
|
262
|
-
/* @__PURE__ */
|
|
262
|
+
/* @__PURE__ */ f("div", {
|
|
263
263
|
className: "mb-6 p-4 rounded-lg bg-[var(--color-status-success-bg-subtle)] border border-[var(--color-status-success-border)]",
|
|
264
|
-
children: /* @__PURE__ */
|
|
264
|
+
children: /* @__PURE__ */ f("p", {
|
|
265
265
|
className: "text-sm text-[var(--color-status-success-text)]",
|
|
266
266
|
children: v("auth.mfaSetup.verificationOnSignIn", "You'll be asked for a verification code from your authenticator app when signing in.")
|
|
267
267
|
})
|
|
268
268
|
}),
|
|
269
|
-
/* @__PURE__ */
|
|
269
|
+
/* @__PURE__ */ f("button", {
|
|
270
270
|
type: "button",
|
|
271
271
|
onClick: () => L(!0),
|
|
272
272
|
disabled: C,
|
|
273
273
|
className: "w-full py-3 px-4 rounded-xl border border-[var(--color-status-error-border)] text-[var(--color-status-error-text)] font-medium bg-[var(--color-bg-surface)] hover:bg-[var(--color-status-error-bg-subtle)] disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200 flex items-center justify-center gap-2",
|
|
274
|
-
children: C ? /* @__PURE__ */
|
|
274
|
+
children: C ? /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f(r, { className: "size-5 animate-spin" }), /* @__PURE__ */ f("span", { children: v("auth.mfa.disabling", "Disabling...") })] }) : v("auth.mfaSetup.disableTwoFactor", "Disable Two-Factor Authentication")
|
|
275
275
|
}),
|
|
276
|
-
/* @__PURE__ */
|
|
276
|
+
/* @__PURE__ */ f(h, {
|
|
277
277
|
className: "mt-6",
|
|
278
278
|
storageKey: "auth-mfa-enabled",
|
|
279
279
|
steps: [
|
|
@@ -297,82 +297,82 @@ function v() {
|
|
|
297
297
|
}
|
|
298
298
|
]
|
|
299
299
|
})
|
|
300
|
-
] }) : /* @__PURE__ */
|
|
301
|
-
/* @__PURE__ */
|
|
300
|
+
] }) : /* @__PURE__ */ p(d, { children: [
|
|
301
|
+
/* @__PURE__ */ p("div", {
|
|
302
302
|
className: "flex items-center gap-4 mb-6",
|
|
303
|
-
children: [/* @__PURE__ */
|
|
303
|
+
children: [/* @__PURE__ */ f("div", {
|
|
304
304
|
className: "size-12 rounded-full bg-[var(--color-bg-muted)] flex items-center justify-center",
|
|
305
|
-
children: /* @__PURE__ */
|
|
305
|
+
children: /* @__PURE__ */ f("svg", {
|
|
306
306
|
className: "size-6 text-[var(--color-text-tertiary)]",
|
|
307
307
|
fill: "none",
|
|
308
308
|
viewBox: "0 0 24 24",
|
|
309
309
|
stroke: "currentColor",
|
|
310
|
-
children: /* @__PURE__ */
|
|
310
|
+
children: /* @__PURE__ */ f("path", {
|
|
311
311
|
strokeLinecap: "round",
|
|
312
312
|
strokeLinejoin: "round",
|
|
313
313
|
strokeWidth: 2,
|
|
314
314
|
d: "M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"
|
|
315
315
|
})
|
|
316
316
|
})
|
|
317
|
-
}), /* @__PURE__ */
|
|
317
|
+
}), /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("h2", {
|
|
318
318
|
className: "text-xl font-semibold text-[var(--color-text-primary)]",
|
|
319
319
|
children: v("auth.mfaSetup.twoFactorOff", "Two-Factor Authentication is Off")
|
|
320
|
-
}), /* @__PURE__ */
|
|
320
|
+
}), /* @__PURE__ */ f("p", {
|
|
321
321
|
className: "text-[var(--color-text-secondary)]",
|
|
322
322
|
children: v("auth.mfaSetup.notProtected", "Your account is not protected with 2FA")
|
|
323
323
|
})] })]
|
|
324
324
|
}),
|
|
325
|
-
/* @__PURE__ */
|
|
325
|
+
/* @__PURE__ */ p("div", {
|
|
326
326
|
className: "mb-6 p-4 rounded-lg bg-[var(--color-bg-accent)] border border-[var(--color-border-default)]",
|
|
327
|
-
children: [/* @__PURE__ */
|
|
327
|
+
children: [/* @__PURE__ */ f("h3", {
|
|
328
328
|
className: "font-medium text-[var(--color-text-link)] mb-2",
|
|
329
329
|
children: v("auth.mfaSetup.whyEnable", "Why enable 2FA?")
|
|
330
|
-
}), /* @__PURE__ */
|
|
330
|
+
}), /* @__PURE__ */ p("ul", {
|
|
331
331
|
className: "text-sm text-[var(--color-text-link)] space-y-1",
|
|
332
332
|
children: [
|
|
333
|
-
/* @__PURE__ */
|
|
334
|
-
/* @__PURE__ */
|
|
335
|
-
/* @__PURE__ */
|
|
333
|
+
/* @__PURE__ */ f("li", { children: "• Protects your account even if your password is compromised" }),
|
|
334
|
+
/* @__PURE__ */ f("li", { children: "• Adds a second verification step during sign-in" }),
|
|
335
|
+
/* @__PURE__ */ f("li", { children: "• Works with popular authenticator apps (Google, Authy, etc.)" })
|
|
336
336
|
]
|
|
337
337
|
})]
|
|
338
338
|
}),
|
|
339
|
-
/* @__PURE__ */
|
|
339
|
+
/* @__PURE__ */ f(m, {
|
|
340
340
|
type: "button",
|
|
341
341
|
onClick: () => R("TOTP"),
|
|
342
342
|
disabled: C,
|
|
343
343
|
variant: "default",
|
|
344
344
|
size: "lg",
|
|
345
345
|
className: "w-full font-semibold",
|
|
346
|
-
children: C ? /* @__PURE__ */
|
|
346
|
+
children: C ? /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f(r, { className: "size-5 animate-spin" }), /* @__PURE__ */ f("span", { children: v("auth.mfa.settingUp", "Setting up...") })] }) : /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f("svg", {
|
|
347
347
|
className: "size-5",
|
|
348
348
|
fill: "none",
|
|
349
349
|
viewBox: "0 0 24 24",
|
|
350
350
|
stroke: "currentColor",
|
|
351
|
-
children: /* @__PURE__ */
|
|
351
|
+
children: /* @__PURE__ */ f("path", {
|
|
352
352
|
strokeLinecap: "round",
|
|
353
353
|
strokeLinejoin: "round",
|
|
354
354
|
strokeWidth: 2,
|
|
355
355
|
d: "M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
|
|
356
356
|
})
|
|
357
|
-
}), /* @__PURE__ */
|
|
357
|
+
}), /* @__PURE__ */ f("span", { children: v("auth.mfaSetup.enableTwoFactor", "Enable Two-Factor Authentication") })] })
|
|
358
358
|
})
|
|
359
359
|
] })
|
|
360
360
|
})
|
|
361
361
|
]
|
|
362
|
-
}) : /* @__PURE__ */
|
|
362
|
+
}) : /* @__PURE__ */ f("div", {
|
|
363
363
|
className: "max-w-2xl mx-auto p-6",
|
|
364
|
-
children: /* @__PURE__ */
|
|
364
|
+
children: /* @__PURE__ */ p("div", {
|
|
365
365
|
className: "text-center py-16 border-2 border-dashed border-border-default rounded-xl",
|
|
366
366
|
children: [
|
|
367
|
-
/* @__PURE__ */
|
|
367
|
+
/* @__PURE__ */ f("div", {
|
|
368
368
|
className: "size-16 mx-auto mb-4 rounded-full bg-status-warning-bg-subtle flex items-center justify-center",
|
|
369
|
-
children: /* @__PURE__ */
|
|
369
|
+
children: /* @__PURE__ */ f(n, { className: "size-8 text-status-warning-text" })
|
|
370
370
|
}),
|
|
371
|
-
/* @__PURE__ */
|
|
371
|
+
/* @__PURE__ */ f("h2", {
|
|
372
372
|
className: "text-xl font-semibold text-text-primary mb-2",
|
|
373
373
|
children: v("auth.mfaSetup.authRequired", "Authentication Required")
|
|
374
374
|
}),
|
|
375
|
-
/* @__PURE__ */
|
|
375
|
+
/* @__PURE__ */ f("p", {
|
|
376
376
|
className: "text-text-secondary",
|
|
377
377
|
children: v("auth.mfaSetup.signInToManage", "Please sign in to manage MFA settings")
|
|
378
378
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MfaSetupPage.js","names":[],"sources":["../../../src/auth/pages/MfaSetupPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { useNavigate } from \"react-router\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport { Button } from \"@burdenoff/fe-libs/button\";\nimport { PagePurpose, NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport { QRCodeSVG } from \"qrcode.react\";\nimport { useMfaOperations } from \"../hooks/useMfaOperations\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { ConfirmActionDialog } from \"../components/ConfirmActionDialog\";\nimport { SpinnerIcon, AlertIcon } from \"../components/AuthIcons\";\nimport type { MFAMethod, MFASetupResult } from \"../types\";\nimport {\n nativeCopyText,\n nativeImpact,\n nativeNotify,\n} from \"../../utils/nativeBridge\";\n\n/**\n * MFA setup and management page with professional UI\n */\nexport function MfaSetupPage() {\n const tr = useTr();\n\n const navigate = useNavigate();\n const { setupMFA, enableMFA, disableMFA, isLoading } = useMfaOperations();\n const user = useAuthStore((state) => state.user);\n const isAuthenticated = useAuthStore((state) => state.isAuthenticated);\n const [setupData, setSetupData] = useState<MFASetupResult | null>(null);\n const [verificationCode, setVerificationCode] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [backupCodesCopied, setBackupCodesCopied] = useState(false);\n const [showDisableDialog, setShowDisableDialog] = useState(false);\n\n const handleSetupMfa = async (method: MFAMethod) => {\n try {\n setError(null);\n setSuccessMessage(null);\n const data = await setupMFA(method);\n setSetupData(data);\n } catch (err) {\n console.error(\"Failed to setup MFA:\", err);\n setError(\n err instanceof Error\n ? err.message\n : \"Failed to setup MFA. Please try again.\",\n );\n }\n };\n\n const handleVerifyAndEnable = async () => {\n if (verificationCode.length !== 6) {\n setError(tr(\"auth.mfaSetup.enter6Digit\", \"Please enter a 6-digit code\"));\n return;\n }\n\n try {\n setError(null);\n const success = await enableMFA(verificationCode);\n if (success) {\n setSetupData(null);\n setVerificationCode(\"\");\n setSuccessMessage(\n tr(\n \"auth.mfaSetup.enableSuccess\",\n \"Two-factor authentication has been enabled successfully!\",\n ),\n );\n }\n } catch (err) {\n console.error(\"Failed to verify MFA code:\", err);\n setError(\n err instanceof Error\n ? err.message\n : \"Invalid verification code. Please try again.\",\n );\n }\n };\n\n const handleDisableMfa = async () => {\n try {\n setError(null);\n setSuccessMessage(null);\n await disableMFA();\n setSuccessMessage(\n tr(\n \"auth.mfaSetup.disableSuccess\",\n \"Two-factor authentication has been disabled.\",\n ),\n );\n setShowDisableDialog(false);\n } catch (err) {\n console.error(\"Failed to disable MFA:\", err);\n setError(\n err instanceof Error\n ? err.message\n : \"Failed to disable MFA. Please try again.\",\n );\n }\n };\n\n const copyBackupCodes = async () => {\n if (setupData?.backupCodes) {\n void nativeImpact(\"light\");\n const ok = await nativeCopyText(setupData.backupCodes.join(\"\\n\"));\n if (ok) {\n setBackupCodesCopied(true);\n void nativeNotify(\"success\");\n setTimeout(() => setBackupCodesCopied(false), 2000);\n } else {\n void nativeNotify(\"error\");\n }\n }\n };\n\n const cancelSetup = () => {\n setSetupData(null);\n setVerificationCode(\"\");\n setError(null);\n };\n\n // Authentication check\n if (!isAuthenticated) {\n return (\n <div className=\"max-w-2xl mx-auto p-6\">\n <div className=\"text-center py-16 border-2 border-dashed border-border-default rounded-xl\">\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-status-warning-bg-subtle flex items-center justify-center\">\n <AlertIcon className=\"size-8 text-status-warning-text\" />\n </div>\n <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr(\"auth.mfaSetup.authRequired\", \"Authentication Required\")}\n </h2>\n <p className=\"text-text-secondary\">\n {tr(\n \"auth.mfaSetup.signInToManage\",\n \"Please sign in to manage MFA settings\",\n )}\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"max-w-2xl mx-auto p-6\">\n <ConfirmActionDialog\n open={showDisableDialog}\n onOpenChange={setShowDisableDialog}\n title={tr(\n \"auth.mfaSetup.disableTitle\",\n \"Disable two-factor authentication?\",\n )}\n description={tr(\n \"auth.mfaSetup.disableDesc\",\n \"Your account will become less secure and future sign-ins will only require your password until MFA is re-enabled.\",\n )}\n confirmText={tr(\"auth.mfaSetup.disableConfirm\", \"Disable MFA\")}\n variant=\"warning\"\n isPending={isLoading}\n onConfirm={handleDisableMfa}\n />\n {/* Header */}\n <div className=\"mb-8\">\n <h1 className=\"text-3xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\"auth.mfaSetup.title\", \"Two-Factor Authentication\")}\n </h1>\n <p className=\"text-[var(--color-text-secondary)] mt-2\">\n {tr(\n \"auth.mfaSetup.subtitle\",\n \"Add an extra layer of security to your account\",\n )}\n </p>\n <PagePurpose className=\"mt-3\">\n {tr(\n \"auth.mfaSetup.purpose\",\n \"Two-factor authentication (2FA) asks for a one-time code from an authenticator app at sign-in, so your account stays safe even if your password leaks. Set it up here and keep your backup codes somewhere safe.\",\n )}\n </PagePurpose>\n </div>\n\n {/* Success Message */}\n {successMessage && (\n <div className=\"mb-6 p-4 rounded-xl bg-[var(--color-status-success-bg-subtle)] border border-[var(--color-status-success-border)]\">\n <div className=\"flex items-center gap-3\">\n <svg\n className=\"size-5 text-[var(--color-status-success-text)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <p className=\"text-[var(--color-status-success-text)]\">\n {successMessage}\n </p>\n </div>\n </div>\n )}\n\n {/* Error Message */}\n {error && (\n <div className=\"mb-6 flex items-start gap-3 p-4 rounded-xl bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)]\">\n <AlertIcon className=\"size-5 text-[var(--color-status-error-text)] flex-shrink-0 mt-0.5\" />\n <div className=\"flex-1\">\n <p className=\"text-sm text-[var(--color-status-error-text)]\">\n {error}\n </p>\n <button\n type=\"button\"\n onClick={() => setError(null)}\n className=\"mt-2 text-sm text-[var(--color-status-error-text)] hover:underline transition-colors duration-200\"\n >\n {tr(\"auth.mfaSetup.dismiss\", \"Dismiss\")}\n </button>\n </div>\n </div>\n )}\n\n {setupData ? (\n /* Setup Flow */\n <div className=\"p-6 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-sm\">\n <div className=\"flex items-center gap-3 mb-6\">\n <div className=\"size-10 rounded-full bg-[var(--color-bg-accent)] flex items-center justify-center\">\n <svg\n className=\"size-5 text-[var(--color-text-link)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <div>\n <h2 className=\"text-xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\"auth.mfaSetup.setupAuthApp\", \"Setup Authenticator App\")}\n </h2>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr(\n \"auth.mfaSetup.scanQr\",\n \"Scan the QR code with your authenticator app\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"space-y-6\">\n {/* QR Code */}\n {setupData.qrCode && (\n <div className=\"flex justify-center\">\n <div className=\"p-4 bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)]\">\n <QRCodeSVG\n value={setupData.qrCode}\n size={192}\n level=\"M\"\n includeMargin={false}\n className=\"size-48\"\n />\n </div>\n </div>\n )}\n\n {/* Manual Secret */}\n {setupData.secret && (\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)] mb-2\">\n {tr(\n \"auth.mfaSetup.cantScan\",\n \"Can't scan the QR code? Enter this key manually:\",\n )}\n </p>\n <code className=\"block p-3 rounded-lg bg-[var(--color-bg-muted)] text-[var(--color-text-primary)] font-mono text-sm break-all select-all\">\n {setupData.secret}\n </code>\n </div>\n )}\n\n {/* Backup Codes */}\n {setupData.backupCodes && setupData.backupCodes.length > 0 && (\n <div className=\"p-4 rounded-xl bg-[var(--color-status-warning-bg-subtle)] border border-[var(--color-status-warning-border)]\">\n <div className=\"flex items-start gap-3 mb-3\">\n <svg\n className=\"size-5 text-[var(--color-status-warning-text)] flex-shrink-0 mt-0.5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n <div>\n <h3 className=\"font-medium text-[var(--color-status-warning-text)]\">\n {tr(\n \"auth.mfaSetup.saveBackupCodes\",\n \"Save your backup codes\",\n )}\n </h3>\n <p className=\"text-sm text-[var(--color-status-warning-text)]\">\n {tr(\n \"auth.mfaSetup.storeCodesSafely\",\n \"Store these codes safely. You can use them to access your account if you lose your authenticator device.\",\n )}\n </p>\n </div>\n </div>\n <div className=\"p-3 rounded-lg bg-[var(--color-bg-surface)] grid grid-cols-2 gap-2 mb-3\">\n {setupData.backupCodes.map((code, i) => (\n <code\n key={code}\n className=\"text-sm text-[var(--color-text-primary)] font-mono\"\n >\n {i + 1}. {code}\n </code>\n ))}\n </div>\n <button\n type=\"button\"\n onClick={copyBackupCodes}\n className=\"text-sm font-medium text-[var(--color-status-warning-text)] hover:underline transition-colors duration-200\"\n >\n {backupCodesCopied\n ? tr(\"auth.mfaSetup.copied\", \"Copied!\")\n : tr(\"auth.mfaSetup.copyBackupCodes\", \"Copy backup codes\")}\n </button>\n </div>\n )}\n\n {/* Verification Code Input */}\n <div>\n <label\n htmlFor=\"mfa-verification-code\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-2\"\n >\n {tr(\n \"auth.mfaSetup.enterCode\",\n \"Enter the 6-digit code from your app\",\n )}\n </label>\n <input\n id=\"mfa-verification-code\"\n type=\"text\"\n inputMode=\"numeric\"\n maxLength={6}\n value={verificationCode}\n onChange={(e) =>\n setVerificationCode(e.target.value.replace(/\\D/g, \"\"))\n }\n placeholder={tr(\n \"auth.mfa.verificationCodePlaceholder\",\n \"000000\",\n )}\n aria-label={tr(\n \"auth.mfaSetup.enterCode\",\n \"Enter the 6-digit code from your app\",\n )}\n aria-required=\"true\"\n autoComplete=\"one-time-code\"\n className=\"block w-full px-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] text-center text-2xl tracking-widest font-mono focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all duration-300\"\n />\n </div>\n\n {/* Action Buttons */}\n <div className=\"flex gap-3\">\n <button\n type=\"button\"\n onClick={cancelSetup}\n className=\"flex-1 py-3 px-4 rounded-xl border border-[var(--color-border-default)] text-[var(--color-text-secondary)] font-medium hover:bg-[var(--color-bg-muted)] transition-colors duration-200\"\n >\n {tr(\"auth.mfaSetup.cancel\", \"Cancel\")}\n </button>\n <Button\n type=\"button\"\n onClick={handleVerifyAndEnable}\n disabled={isLoading || verificationCode.length !== 6}\n variant=\"default\"\n size=\"lg\"\n className=\"flex-1 font-semibold\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"size-5 animate-spin\" />\n <span>{tr(\"auth.mfa.verifying\", \"Verifying...\")}</span>\n </>\n ) : (\n tr(\"auth.mfaSetup.enable2fa\", \"Enable 2FA\")\n )}\n </Button>\n </div>\n </div>\n </div>\n ) : (\n /* Status Display */\n <div className=\"p-6 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-sm\">\n {!user?.mfaEnabled ? (\n /* MFA Not Enabled */\n <>\n <div className=\"flex items-center gap-4 mb-6\">\n <div className=\"size-12 rounded-full bg-[var(--color-bg-muted)] flex items-center justify-center\">\n <svg\n className=\"size-6 text-[var(--color-text-tertiary)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z\"\n />\n </svg>\n </div>\n <div>\n <h2 className=\"text-xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\n \"auth.mfaSetup.twoFactorOff\",\n \"Two-Factor Authentication is Off\",\n )}\n </h2>\n <p className=\"text-[var(--color-text-secondary)]\">\n {tr(\n \"auth.mfaSetup.notProtected\",\n \"Your account is not protected with 2FA\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"mb-6 p-4 rounded-lg bg-[var(--color-bg-accent)] border border-[var(--color-border-default)]\">\n <h3 className=\"font-medium text-[var(--color-text-link)] mb-2\">\n {tr(\"auth.mfaSetup.whyEnable\", \"Why enable 2FA?\")}\n </h3>\n <ul className=\"text-sm text-[var(--color-text-link)] space-y-1\">\n <li>\n • Protects your account even if your password is compromised\n </li>\n <li>• Adds a second verification step during sign-in</li>\n <li>\n • Works with popular authenticator apps (Google, Authy,\n etc.)\n </li>\n </ul>\n </div>\n\n <Button\n type=\"button\"\n onClick={() => handleSetupMfa(\"TOTP\")}\n disabled={isLoading}\n variant=\"default\"\n size=\"lg\"\n className=\"w-full font-semibold\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"size-5 animate-spin\" />\n <span>{tr(\"auth.mfa.settingUp\", \"Setting up...\")}</span>\n </>\n ) : (\n <>\n <svg\n className=\"size-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n <span>\n {tr(\n \"auth.mfaSetup.enableTwoFactor\",\n \"Enable Two-Factor Authentication\",\n )}\n </span>\n </>\n )}\n </Button>\n </>\n ) : (\n /* MFA Enabled */\n <>\n <div className=\"flex items-center gap-4 mb-6\">\n <div className=\"size-12 rounded-full bg-[var(--color-status-success-bg-subtle)] flex items-center justify-center\">\n <svg\n className=\"size-6 text-[var(--color-status-success-text)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z\"\n />\n </svg>\n </div>\n <div>\n <h2 className=\"text-xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\n \"auth.mfaSetup.twoFactorOn\",\n \"Two-Factor Authentication is On\",\n )}\n </h2>\n <p className=\"text-[var(--color-text-secondary)]\">\n {tr(\n \"auth.mfaSetup.protected\",\n \"Your account is protected with an authenticator app\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"mb-6 p-4 rounded-lg bg-[var(--color-status-success-bg-subtle)] border border-[var(--color-status-success-border)]\">\n <p className=\"text-sm text-[var(--color-status-success-text)]\">\n {tr(\n \"auth.mfaSetup.verificationOnSignIn\",\n \"You'll be asked for a verification code from your authenticator app when signing in.\",\n )}\n </p>\n </div>\n\n <button\n type=\"button\"\n onClick={() => setShowDisableDialog(true)}\n disabled={isLoading}\n className=\"w-full py-3 px-4 rounded-xl border border-[var(--color-status-error-border)] text-[var(--color-status-error-text)] font-medium bg-[var(--color-bg-surface)] hover:bg-[var(--color-status-error-bg-subtle)] disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200 flex items-center justify-center gap-2\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"size-5 animate-spin\" />\n <span>{tr(\"auth.mfa.disabling\", \"Disabling...\")}</span>\n </>\n ) : (\n tr(\n \"auth.mfaSetup.disableTwoFactor\",\n \"Disable Two-Factor Authentication\",\n )\n )}\n </button>\n\n <NextSteps\n className=\"mt-6\"\n storageKey=\"auth-mfa-enabled\"\n steps={[\n {\n id: \"review-sessions\",\n label: tr(\n \"auth.mfaSetup.nextReviewSessions\",\n \"Review your active sessions\",\n ),\n description: tr(\n \"auth.mfaSetup.nextReviewSessionsDesc\",\n \"Sign out any device you no longer recognise.\",\n ),\n onClick: () => navigate(\"/auth/sessions\"),\n },\n {\n id: \"security-center\",\n label: tr(\n \"auth.mfaSetup.nextSecurityCenter\",\n \"Visit the Security Center\",\n ),\n description: tr(\n \"auth.mfaSetup.nextSecurityCenterDesc\",\n \"Check your password and account protection at a glance.\",\n ),\n onClick: () => navigate(\"/auth/security\"),\n },\n {\n id: \"update-profile\",\n label: tr(\n \"auth.mfaSetup.nextProfile\",\n \"Keep your profile up to date\",\n ),\n description: tr(\n \"auth.mfaSetup.nextProfileDesc\",\n \"Confirm your recovery email and contact details.\",\n ),\n onClick: () => navigate(\"/auth/profile\"),\n },\n ]}\n />\n </>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MfaSetupPage.js","names":[],"sources":["../../../src/auth/pages/MfaSetupPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { useNavigate } from \"react-router\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport { Button } from \"@burdenoff/fe-libs/button\";\nimport { PagePurpose, NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport { QRCodeSVG } from \"qrcode.react\";\nimport { useMfaOperations } from \"../hooks/useMfaOperations\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { ConfirmActionDialog } from \"../components/ConfirmActionDialog\";\nimport { SpinnerIcon, AlertIcon } from \"../components/AuthIcons\";\nimport type { MFAMethod, MFASetupResult } from \"../types\";\nimport {\n nativeCopyText,\n nativeImpact,\n nativeNotify,\n} from \"../../utils/nativeBridge\";\n\n/**\n * MFA setup and management page with professional UI\n */\nexport function MfaSetupPage() {\n const tr = useTr();\n\n const navigate = useNavigate();\n const { setupMFA, enableMFA, disableMFA, isLoading } = useMfaOperations();\n const user = useAuthStore((state) => state.user);\n const isAuthenticated = useAuthStore((state) => state.isAuthenticated);\n const [setupData, setSetupData] = useState<MFASetupResult | null>(null);\n const [verificationCode, setVerificationCode] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [backupCodesCopied, setBackupCodesCopied] = useState(false);\n const [showDisableDialog, setShowDisableDialog] = useState(false);\n\n const handleSetupMfa = async (method: MFAMethod) => {\n try {\n setError(null);\n setSuccessMessage(null);\n const data = await setupMFA(method);\n setSetupData(data);\n } catch (err) {\n console.error(\"Failed to setup MFA:\", err);\n setError(\n err instanceof Error\n ? err.message\n : \"Failed to setup MFA. Please try again.\",\n );\n }\n };\n\n const handleVerifyAndEnable = async () => {\n if (verificationCode.length !== 6) {\n setError(tr(\"auth.mfaSetup.enter6Digit\", \"Please enter a 6-digit code\"));\n return;\n }\n\n try {\n setError(null);\n const success = await enableMFA(verificationCode);\n if (success) {\n setSetupData(null);\n setVerificationCode(\"\");\n setSuccessMessage(\n tr(\n \"auth.mfaSetup.enableSuccess\",\n \"Two-factor authentication has been enabled successfully!\",\n ),\n );\n }\n } catch (err) {\n console.error(\"Failed to verify MFA code:\", err);\n setError(\n err instanceof Error\n ? err.message\n : \"Invalid verification code. Please try again.\",\n );\n }\n };\n\n const handleDisableMfa = async () => {\n try {\n setError(null);\n setSuccessMessage(null);\n await disableMFA();\n setSuccessMessage(\n tr(\n \"auth.mfaSetup.disableSuccess\",\n \"Two-factor authentication has been disabled.\",\n ),\n );\n setShowDisableDialog(false);\n } catch (err) {\n console.error(\"Failed to disable MFA:\", err);\n setError(\n err instanceof Error\n ? err.message\n : \"Failed to disable MFA. Please try again.\",\n );\n }\n };\n\n const copyBackupCodes = async () => {\n if (setupData?.backupCodes) {\n void nativeImpact(\"light\");\n const ok = await nativeCopyText(setupData.backupCodes.join(\"\\n\"));\n if (ok) {\n setBackupCodesCopied(true);\n void nativeNotify(\"success\");\n setTimeout(() => setBackupCodesCopied(false), 2000);\n } else {\n void nativeNotify(\"error\");\n }\n }\n };\n\n const cancelSetup = () => {\n setSetupData(null);\n setVerificationCode(\"\");\n setError(null);\n };\n\n // Authentication check\n if (!isAuthenticated) {\n return (\n <div className=\"max-w-2xl mx-auto p-6\">\n <div className=\"text-center py-16 border-2 border-dashed border-border-default rounded-xl\">\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-status-warning-bg-subtle flex items-center justify-center\">\n <AlertIcon className=\"size-8 text-status-warning-text\" />\n </div>\n <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr(\"auth.mfaSetup.authRequired\", \"Authentication Required\")}\n </h2>\n <p className=\"text-text-secondary\">\n {tr(\n \"auth.mfaSetup.signInToManage\",\n \"Please sign in to manage MFA settings\",\n )}\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"max-w-2xl mx-auto p-6\">\n <ConfirmActionDialog\n open={showDisableDialog}\n onOpenChange={setShowDisableDialog}\n title={tr(\n \"auth.mfaSetup.disableTitle\",\n \"Disable two-factor authentication?\",\n )}\n description={tr(\n \"auth.mfaSetup.disableDesc\",\n \"Your account will become less secure and future sign-ins will only require your password until MFA is re-enabled.\",\n )}\n confirmText={tr(\"auth.mfaSetup.disableConfirm\", \"Disable MFA\")}\n variant=\"warning\"\n isPending={isLoading}\n onConfirm={handleDisableMfa}\n />\n {/* Header */}\n <div className=\"mb-8\">\n <h1 className=\"text-3xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\"auth.mfaSetup.title\", \"Two-Factor Authentication\")}\n </h1>\n <p className=\"text-[var(--color-text-secondary)] mt-2\">\n {tr(\n \"auth.mfaSetup.subtitle\",\n \"Add an extra layer of security to your account\",\n )}\n </p>\n <PagePurpose className=\"mt-3\">\n {tr(\n \"auth.mfaSetup.purpose\",\n \"Two-factor authentication (2FA) asks for a one-time code from an authenticator app at sign-in, so your account stays safe even if your password leaks. Set it up here and keep your backup codes somewhere safe.\",\n )}\n </PagePurpose>\n </div>\n\n {/* Success Message */}\n {successMessage && (\n <div className=\"mb-6 p-4 rounded-xl bg-[var(--color-status-success-bg-subtle)] border border-[var(--color-status-success-border)]\">\n <div className=\"flex items-center gap-3\">\n <svg\n className=\"size-5 text-[var(--color-status-success-text)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <p className=\"text-[var(--color-status-success-text)]\">\n {successMessage}\n </p>\n </div>\n </div>\n )}\n\n {/* Error Message */}\n {error && (\n <div className=\"mb-6 flex items-start gap-3 p-4 rounded-xl bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)]\">\n <AlertIcon className=\"size-5 text-[var(--color-status-error-text)] flex-shrink-0 mt-0.5\" />\n <div className=\"flex-1\">\n <p className=\"text-sm text-[var(--color-status-error-text)]\">\n {error}\n </p>\n <button\n type=\"button\"\n onClick={() => setError(null)}\n className=\"mt-2 text-sm text-[var(--color-status-error-text)] hover:underline transition-colors duration-200\"\n >\n {tr(\"auth.mfaSetup.dismiss\", \"Dismiss\")}\n </button>\n </div>\n </div>\n )}\n\n {setupData ? (\n /* Setup Flow */\n <div className=\"p-6 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-sm\">\n <div className=\"flex items-center gap-3 mb-6\">\n <div className=\"size-10 rounded-full bg-[var(--color-bg-accent)] flex items-center justify-center\">\n <svg\n className=\"size-5 text-[var(--color-text-link)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <div>\n <h2 className=\"text-xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\"auth.mfaSetup.setupAuthApp\", \"Setup Authenticator App\")}\n </h2>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr(\n \"auth.mfaSetup.scanQr\",\n \"Scan the QR code with your authenticator app\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"space-y-6\">\n {/* QR Code */}\n {setupData.qrCode && (\n <div className=\"flex justify-center\">\n <div className=\"p-4 bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)]\">\n <QRCodeSVG\n value={setupData.qrCode}\n size={192}\n level=\"M\"\n includeMargin={false}\n className=\"size-48\"\n />\n </div>\n </div>\n )}\n\n {/* Manual Secret */}\n {setupData.secret && (\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)] mb-2\">\n {tr(\n \"auth.mfaSetup.cantScan\",\n \"Can't scan the QR code? Enter this key manually:\",\n )}\n </p>\n <code className=\"block p-3 rounded-lg bg-[var(--color-bg-muted)] text-[var(--color-text-primary)] font-mono text-sm break-all select-all\">\n {setupData.secret}\n </code>\n </div>\n )}\n\n {/* Backup Codes */}\n {setupData.backupCodes && setupData.backupCodes.length > 0 && (\n <div className=\"p-4 rounded-xl bg-[var(--color-status-warning-bg-subtle)] border border-[var(--color-status-warning-border)]\">\n <div className=\"flex items-start gap-3 mb-3\">\n <svg\n className=\"size-5 text-[var(--color-status-warning-text)] flex-shrink-0 mt-0.5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n <div>\n <h3 className=\"font-medium text-[var(--color-status-warning-text)]\">\n {tr(\n \"auth.mfaSetup.saveBackupCodes\",\n \"Save your backup codes\",\n )}\n </h3>\n <p className=\"text-sm text-[var(--color-status-warning-text)]\">\n {tr(\n \"auth.mfaSetup.storeCodesSafely\",\n \"Store these codes safely. You can use them to access your account if you lose your authenticator device.\",\n )}\n </p>\n </div>\n </div>\n <div className=\"p-3 rounded-lg bg-[var(--color-bg-surface)] grid grid-cols-2 gap-2 mb-3\">\n {setupData.backupCodes.map((code, i) => (\n <code\n key={code}\n className=\"text-sm text-[var(--color-text-primary)] font-mono\"\n >\n {i + 1}. {code}\n </code>\n ))}\n </div>\n <button\n type=\"button\"\n onClick={copyBackupCodes}\n className=\"text-sm font-medium text-[var(--color-status-warning-text)] hover:underline transition-colors duration-200\"\n >\n {backupCodesCopied\n ? tr(\"auth.mfaSetup.copied\", \"Copied!\")\n : tr(\"auth.mfaSetup.copyBackupCodes\", \"Copy backup codes\")}\n </button>\n </div>\n )}\n\n {/* Verification Code Input */}\n <div>\n <label\n htmlFor=\"mfa-verification-code\"\n className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-2\"\n >\n {tr(\n \"auth.mfaSetup.enterCode\",\n \"Enter the 6-digit code from your app\",\n )}\n </label>\n <input\n id=\"mfa-verification-code\"\n type=\"text\"\n inputMode=\"numeric\"\n maxLength={6}\n value={verificationCode}\n onChange={(e) =>\n setVerificationCode(e.target.value.replace(/\\D/g, \"\"))\n }\n placeholder={tr(\n \"auth.mfa.verificationCodePlaceholder\",\n \"000000\",\n )}\n aria-label={tr(\n \"auth.mfaSetup.enterCode\",\n \"Enter the 6-digit code from your app\",\n )}\n aria-required=\"true\"\n autoComplete=\"one-time-code\"\n className=\"block w-full px-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] text-center text-2xl tracking-widest font-mono focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all duration-300\"\n />\n </div>\n\n {/* Action Buttons */}\n <div className=\"flex gap-3\">\n <button\n type=\"button\"\n onClick={cancelSetup}\n className=\"flex-1 py-3 px-4 rounded-xl border border-[var(--color-border-default)] text-[var(--color-text-secondary)] font-medium hover:bg-[var(--color-bg-muted)] transition-colors duration-200\"\n >\n {tr(\"auth.mfaSetup.cancel\", \"Cancel\")}\n </button>\n <Button\n type=\"button\"\n onClick={handleVerifyAndEnable}\n disabled={isLoading || verificationCode.length !== 6}\n variant=\"default\"\n size=\"lg\"\n className=\"flex-1 font-semibold\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"size-5 animate-spin\" />\n <span>{tr(\"auth.mfa.verifying\", \"Verifying...\")}</span>\n </>\n ) : (\n tr(\"auth.mfaSetup.enable2fa\", \"Enable 2FA\")\n )}\n </Button>\n </div>\n </div>\n </div>\n ) : (\n /* Status Display */\n <div className=\"p-6 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-sm\">\n {!user?.mfaEnabled ? (\n /* MFA Not Enabled */\n <>\n <div className=\"flex items-center gap-4 mb-6\">\n <div className=\"size-12 rounded-full bg-[var(--color-bg-muted)] flex items-center justify-center\">\n <svg\n className=\"size-6 text-[var(--color-text-tertiary)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z\"\n />\n </svg>\n </div>\n <div>\n <h2 className=\"text-xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\n \"auth.mfaSetup.twoFactorOff\",\n \"Two-Factor Authentication is Off\",\n )}\n </h2>\n <p className=\"text-[var(--color-text-secondary)]\">\n {tr(\n \"auth.mfaSetup.notProtected\",\n \"Your account is not protected with 2FA\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"mb-6 p-4 rounded-lg bg-[var(--color-bg-accent)] border border-[var(--color-border-default)]\">\n <h3 className=\"font-medium text-[var(--color-text-link)] mb-2\">\n {tr(\"auth.mfaSetup.whyEnable\", \"Why enable 2FA?\")}\n </h3>\n <ul className=\"text-sm text-[var(--color-text-link)] space-y-1\">\n <li>\n • Protects your account even if your password is compromised\n </li>\n <li>• Adds a second verification step during sign-in</li>\n <li>\n • Works with popular authenticator apps (Google, Authy,\n etc.)\n </li>\n </ul>\n </div>\n\n <Button\n type=\"button\"\n onClick={() => handleSetupMfa(\"TOTP\")}\n disabled={isLoading}\n variant=\"default\"\n size=\"lg\"\n className=\"w-full font-semibold\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"size-5 animate-spin\" />\n <span>{tr(\"auth.mfa.settingUp\", \"Setting up...\")}</span>\n </>\n ) : (\n <>\n <svg\n className=\"size-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n <span>\n {tr(\n \"auth.mfaSetup.enableTwoFactor\",\n \"Enable Two-Factor Authentication\",\n )}\n </span>\n </>\n )}\n </Button>\n </>\n ) : (\n /* MFA Enabled */\n <>\n <div className=\"flex items-center gap-4 mb-6\">\n <div className=\"size-12 rounded-full bg-[var(--color-status-success-bg-subtle)] flex items-center justify-center\">\n <svg\n className=\"size-6 text-[var(--color-status-success-text)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z\"\n />\n </svg>\n </div>\n <div>\n <h2 className=\"text-xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\n \"auth.mfaSetup.twoFactorOn\",\n \"Two-Factor Authentication is On\",\n )}\n </h2>\n <p className=\"text-[var(--color-text-secondary)]\">\n {tr(\n \"auth.mfaSetup.protected\",\n \"Your account is protected with an authenticator app\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"mb-6 p-4 rounded-lg bg-[var(--color-status-success-bg-subtle)] border border-[var(--color-status-success-border)]\">\n <p className=\"text-sm text-[var(--color-status-success-text)]\">\n {tr(\n \"auth.mfaSetup.verificationOnSignIn\",\n \"You'll be asked for a verification code from your authenticator app when signing in.\",\n )}\n </p>\n </div>\n\n <button\n type=\"button\"\n onClick={() => setShowDisableDialog(true)}\n disabled={isLoading}\n className=\"w-full py-3 px-4 rounded-xl border border-[var(--color-status-error-border)] text-[var(--color-status-error-text)] font-medium bg-[var(--color-bg-surface)] hover:bg-[var(--color-status-error-bg-subtle)] disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200 flex items-center justify-center gap-2\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"size-5 animate-spin\" />\n <span>{tr(\"auth.mfa.disabling\", \"Disabling...\")}</span>\n </>\n ) : (\n tr(\n \"auth.mfaSetup.disableTwoFactor\",\n \"Disable Two-Factor Authentication\",\n )\n )}\n </button>\n\n <NextSteps\n className=\"mt-6\"\n storageKey=\"auth-mfa-enabled\"\n steps={[\n {\n id: \"review-sessions\",\n label: tr(\n \"auth.mfaSetup.nextReviewSessions\",\n \"Review your active sessions\",\n ),\n description: tr(\n \"auth.mfaSetup.nextReviewSessionsDesc\",\n \"Sign out any device you no longer recognise.\",\n ),\n onClick: () => navigate(\"/auth/sessions\"),\n },\n {\n id: \"security-center\",\n label: tr(\n \"auth.mfaSetup.nextSecurityCenter\",\n \"Visit the Security Center\",\n ),\n description: tr(\n \"auth.mfaSetup.nextSecurityCenterDesc\",\n \"Check your password and account protection at a glance.\",\n ),\n onClick: () => navigate(\"/auth/security\"),\n },\n {\n id: \"update-profile\",\n label: tr(\n \"auth.mfaSetup.nextProfile\",\n \"Keep your profile up to date\",\n ),\n description: tr(\n \"auth.mfaSetup.nextProfileDesc\",\n \"Confirm your recovery email and contact details.\",\n ),\n onClick: () => navigate(\"/auth/profile\"),\n },\n ]}\n />\n </>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|