@burdenoff/microfe-auth 2026.1031.1 → 2026.1101.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/AuthRoot.js +14 -11
- package/dist/auth/AuthRoot.js.map +1 -1
- package/dist/auth/AuthRoutes.js +1 -2
- package/dist/auth/AuthRoutes.js.map +1 -1
- package/dist/auth/components/AuthIcons.js +1 -2
- package/dist/auth/components/AuthIcons.js.map +1 -1
- package/dist/auth/components/AuthLayout.js +1 -2
- package/dist/auth/components/AuthLayout.js.map +1 -1
- package/dist/auth/components/CaptchaV2.js +9 -10
- package/dist/auth/components/CaptchaV2.js.map +1 -1
- package/dist/auth/components/ConfirmActionDialog.js +1 -2
- package/dist/auth/components/ConfirmActionDialog.js.map +1 -1
- package/dist/auth/components/profile/UserProfileMenu.js +7 -6
- package/dist/auth/components/profile/UserProfileMenu.js.map +1 -1
- package/dist/auth/components/workspace/WorkspaceSelector.js +1 -2
- package/dist/auth/components/workspace/WorkspaceSelector.js.map +1 -1
- package/dist/auth/constants/permissions.js +1 -2
- package/dist/auth/constants/permissions.js.map +1 -1
- package/dist/auth/constants/sessionKeys.js +1 -2
- package/dist/auth/constants/sessionKeys.js.map +1 -1
- package/dist/auth/hooks/contextDeepLink.js +1 -2
- package/dist/auth/hooks/contextDeepLink.js.map +1 -1
- package/dist/auth/hooks/index.js +12 -11
- package/dist/auth/hooks/useAccountOperations.js +1 -2
- package/dist/auth/hooks/useAccountOperations.js.map +1 -1
- package/dist/auth/hooks/useActiveContextBootstrapMirror.js +1 -2
- package/dist/auth/hooks/useActiveContextBootstrapMirror.js.map +1 -1
- package/dist/auth/hooks/useAuthOperations.js +37 -37
- package/dist/auth/hooks/useAuthOperations.js.map +1 -1
- package/dist/auth/hooks/useAuthPermissions.js +1 -2
- package/dist/auth/hooks/useAuthPermissions.js.map +1 -1
- package/dist/auth/hooks/useCaptchaV3.js +1 -2
- package/dist/auth/hooks/useCaptchaV3.js.map +1 -1
- package/dist/auth/hooks/useContextSwitcherData.js +86 -79
- package/dist/auth/hooks/useContextSwitcherData.js.map +1 -1
- package/dist/auth/hooks/useMfaOperations.js +4 -5
- package/dist/auth/hooks/useMfaOperations.js.map +1 -1
- package/dist/auth/hooks/useMyWorkspaces.js +4 -5
- package/dist/auth/hooks/useMyWorkspaces.js.map +1 -1
- package/dist/auth/hooks/useOAuthOperations.js +11 -12
- package/dist/auth/hooks/useOAuthOperations.js.map +1 -1
- package/dist/auth/hooks/usePasswordOperations.js +1 -2
- package/dist/auth/hooks/usePasswordOperations.js.map +1 -1
- package/dist/auth/hooks/useSessionOperations.js +3 -4
- package/dist/auth/hooks/useSessionOperations.js.map +1 -1
- package/dist/auth/hooks/useSignupCaptcha.js +1 -2
- package/dist/auth/hooks/useSignupCaptcha.js.map +1 -1
- package/dist/auth/hooks/useSsoOperations.js +80 -81
- package/dist/auth/hooks/useSsoOperations.js.map +1 -1
- package/dist/auth/hooks/useTokenRefresh.js +8 -12
- package/dist/auth/hooks/useTokenRefresh.js.map +1 -1
- package/dist/auth/hooks/useUserOperations.js +1 -2
- package/dist/auth/hooks/useUserOperations.js.map +1 -1
- package/dist/auth/hooks/useWorkspaceOperations.js +1 -2
- package/dist/auth/hooks/useWorkspaceOperations.js.map +1 -1
- package/dist/auth/hooks/useWorkspaceToken.js +1 -2
- package/dist/auth/hooks/useWorkspaceToken.js.map +1 -1
- package/dist/auth/index.js +19 -18
- package/dist/auth/pages/AuditLogPage.js +21 -22
- package/dist/auth/pages/AuditLogPage.js.map +1 -1
- package/dist/auth/pages/EmailVerifyPage.js +15 -17
- package/dist/auth/pages/EmailVerifyPage.js.map +1 -1
- package/dist/auth/pages/LoginPage.js +89 -105
- package/dist/auth/pages/LoginPage.js.map +1 -1
- package/dist/auth/pages/MagicLinkPage.js +30 -31
- package/dist/auth/pages/MagicLinkPage.js.map +1 -1
- package/dist/auth/pages/MfaSetupPage.js +18 -20
- package/dist/auth/pages/MfaSetupPage.js.map +1 -1
- package/dist/auth/pages/MfaVerifyPage.js +31 -35
- package/dist/auth/pages/MfaVerifyPage.js.map +1 -1
- package/dist/auth/pages/OAuthCallbackPage.js +4 -11
- package/dist/auth/pages/OAuthCallbackPage.js.map +1 -1
- package/dist/auth/pages/PasswordResetPage.js +20 -23
- package/dist/auth/pages/PasswordResetPage.js.map +1 -1
- package/dist/auth/pages/ReactivatePage.js +14 -18
- package/dist/auth/pages/ReactivatePage.js.map +1 -1
- package/dist/auth/pages/RegisterPage.js +180 -181
- package/dist/auth/pages/RegisterPage.js.map +1 -1
- package/dist/auth/pages/SSOClearPage.js +9 -10
- package/dist/auth/pages/SSOClearPage.js.map +1 -1
- package/dist/auth/pages/SSODepositPage.js +9 -13
- package/dist/auth/pages/SSODepositPage.js.map +1 -1
- package/dist/auth/pages/SSORelayCallbackPage.js +3 -7
- package/dist/auth/pages/SSORelayCallbackPage.js.map +1 -1
- package/dist/auth/pages/SSORelayPage.js +11 -10
- package/dist/auth/pages/SSORelayPage.js.map +1 -1
- package/dist/auth/pages/SecurityPage.js +19 -21
- package/dist/auth/pages/SecurityPage.js.map +1 -1
- package/dist/auth/pages/SessionBridgePage.js +9 -16
- package/dist/auth/pages/SessionBridgePage.js.map +1 -1
- package/dist/auth/pages/SessionsPage.js +17 -18
- package/dist/auth/pages/SessionsPage.js.map +1 -1
- package/dist/auth/pages/SsoCallbackPage.js +17 -22
- package/dist/auth/pages/SsoCallbackPage.js.map +1 -1
- package/dist/auth/pages/SsoConfigPage.js +69 -69
- package/dist/auth/pages/SsoConfigPage.js.map +1 -1
- package/dist/auth/pages/SsoLoginPage.js +9 -10
- package/dist/auth/pages/SsoLoginPage.js.map +1 -1
- package/dist/auth/pages/UserDetailsPage.js +16 -23
- package/dist/auth/pages/UserDetailsPage.js.map +1 -1
- package/dist/auth/pages/UserProfilePage.js +140 -141
- package/dist/auth/pages/UserProfilePage.js.map +1 -1
- package/dist/auth/pages/UsersPage.js +446 -453
- package/dist/auth/pages/UsersPage.js.map +1 -1
- package/dist/auth/providers/AuthProvider.js +1 -2
- package/dist/auth/providers/AuthProvider.js.map +1 -1
- package/dist/auth/providers/CaptchaProvider.js +1 -2
- package/dist/auth/providers/CaptchaProvider.js.map +1 -1
- package/dist/auth/store/authStore.js +5 -12
- package/dist/auth/store/authStore.js.map +1 -1
- package/dist/auth/utils/captchaConfig.js +5 -8
- package/dist/auth/utils/captchaConfig.js.map +1 -1
- package/dist/auth/utils/formatters.js +1 -2
- package/dist/auth/utils/formatters.js.map +1 -1
- package/dist/auth/utils/gatewayBaseUrls.js +1 -2
- package/dist/auth/utils/gatewayBaseUrls.js.map +1 -1
- package/dist/auth/utils/legalUrls.js +2 -3
- package/dist/auth/utils/legalUrls.js.map +1 -1
- package/dist/auth/utils/redirect.js +1 -2
- package/dist/auth/utils/redirect.js.map +1 -1
- package/dist/auth/utils/ssoOrigins.js +2 -3
- package/dist/auth/utils/ssoOrigins.js.map +1 -1
- package/dist/auth/utils/validation.js +34 -36
- package/dist/auth/utils/validation.js.map +1 -1
- package/dist/generated/global-operations.js +2 -3
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/wspace-operations.js +2 -3
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/microfe-auth.css +1 -1
- package/dist/shared/hooks/useTr.js +1 -2
- package/dist/shared/hooks/useTr.js.map +1 -1
- package/dist/translations/en.js +1 -2
- package/dist/translations/en.js.map +1 -1
- package/dist/utils/nativeBridge.js +1 -2
- package/package.json +7 -3
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
+
import { useAuthStore as t } from "../store/authStore.js";
|
|
3
|
+
import { ConfirmActionDialog as ee } from "../components/ConfirmActionDialog.js";
|
|
4
|
+
import { useAuthOperations as te } from "../hooks/useAuthOperations.js";
|
|
5
|
+
import { useAccountOperations as ne } from "../hooks/useAccountOperations.js";
|
|
6
|
+
import { formatDate as re } from "../utils/formatters.js";
|
|
7
7
|
import { nativeImpact as ie, nativeNotify as n } from "../../utils/nativeBridge.js";
|
|
8
8
|
import { useEffect as r, useId as i, useRef as a, useState as o } from "react";
|
|
9
9
|
import { useNavigate as ae } from "react-router";
|
|
10
10
|
import { jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
11
11
|
import { Button as l } from "@burdenoff/fe-libs/button";
|
|
12
12
|
import { EmphasisPanel as u, NextSteps as d, PagePurpose as f } from "@burdenoff/fe-libs/ui";
|
|
13
|
-
|
|
13
|
+
// src/auth/pages/SecurityPage.tsx
|
|
14
14
|
var p = ({ className: e }) => /* @__PURE__ */ s("svg", {
|
|
15
15
|
className: e,
|
|
16
16
|
fill: "none",
|
|
@@ -122,7 +122,7 @@ var p = ({ className: e }) => /* @__PURE__ */ s("svg", {
|
|
|
122
122
|
})]
|
|
123
123
|
}), v = "DELETE MY ACCOUNT";
|
|
124
124
|
function y({ enableAdminRoutes: y = !1 }) {
|
|
125
|
-
let b =
|
|
125
|
+
let b = e(), x = ae(), S = t((e) => e.user), { signOut: C } = te({ disableCurrentUserQuery: !0 }), { deactivateAccount: w, deleteAccount: T, isDeactivating: E, isDeleting: D } = ne(), [ue, O] = o(!1), [de, k] = o(!1), fe = [
|
|
126
126
|
{
|
|
127
127
|
name: b("auth.security.navProfile", "User Profile"),
|
|
128
128
|
description: b("auth.security.navProfileDesc", "View and edit your profile information"),
|
|
@@ -178,23 +178,22 @@ function y({ enableAdminRoutes: y = !1 }) {
|
|
|
178
178
|
z(e instanceof Error ? e.message : b("auth.security.deactivateFailed", "Failed to deactivate account"));
|
|
179
179
|
}
|
|
180
180
|
}, _e = async () => {
|
|
181
|
-
if (z(null), I !== v)
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
n("error"), z(e instanceof Error ? e.message : b("auth.security.deleteFailed", "Failed to delete account"));
|
|
181
|
+
if (z(null), I !== v) z(b("auth.security.deleteConfirmMismatch", "Please type \"DELETE MY ACCOUNT\" to confirm"));
|
|
182
|
+
else {
|
|
183
|
+
ie("medium");
|
|
184
|
+
try {
|
|
185
|
+
let e = await T(P, I);
|
|
186
|
+
e.success ? (n("success"), x("/auth/login", { replace: !0 })) : (n("error"), z(e.message || b("auth.security.deleteFailed", "Failed to delete account")));
|
|
187
|
+
} catch (e) {
|
|
188
|
+
n("error"), z(e instanceof Error ? e.message : b("auth.security.deleteFailed", "Failed to delete account"));
|
|
189
|
+
}
|
|
191
190
|
}
|
|
192
191
|
}, Q = () => {
|
|
193
192
|
j(!1), N(!1), F(""), L(""), z(null), me(null);
|
|
194
193
|
}, $ = S?.role === "SUPER_ADMIN" || S?.role === "TENANT_ADMIN" || S?.role === "ORG_ADMIN", ve = fe.filter((e) => !e.adminOnly || y && $), ye = pe.filter((e) => !e.adminOnly || y && $);
|
|
195
194
|
return /* @__PURE__ */ c("div", {
|
|
196
195
|
className: "bg-bg-canvas p-6",
|
|
197
|
-
children: [/* @__PURE__ */ s(
|
|
196
|
+
children: [/* @__PURE__ */ s(ee, {
|
|
198
197
|
open: de,
|
|
199
198
|
onOpenChange: k,
|
|
200
199
|
title: b("auth.security.signOutDialogTitle", "Sign out now?"),
|
|
@@ -305,7 +304,7 @@ function y({ enableAdminRoutes: y = !1 }) {
|
|
|
305
304
|
children: b("auth.security.memberSince", "Member Since")
|
|
306
305
|
}), /* @__PURE__ */ s("dd", {
|
|
307
306
|
className: "mt-1 text-text-primary",
|
|
308
|
-
children: S?.createdAt ?
|
|
307
|
+
children: S?.createdAt ? re(S.createdAt) : "-"
|
|
309
308
|
})] })
|
|
310
309
|
]
|
|
311
310
|
})]
|
|
@@ -674,7 +673,6 @@ function y({ enableAdminRoutes: y = !1 }) {
|
|
|
674
673
|
})]
|
|
675
674
|
});
|
|
676
675
|
}
|
|
677
|
-
//#endregion
|
|
678
676
|
export { y as SecurityPage };
|
|
679
677
|
|
|
680
678
|
//# sourceMappingURL=SecurityPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityPage.js","names":[],"sources":["../../../src/auth/pages/SecurityPage.tsx"],"sourcesContent":["import { useId, useRef, useEffect, useState } from \"react\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport { Button } from \"@burdenoff/fe-libs/button\";\nimport { EmphasisPanel, PagePurpose, NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport { useNavigate } from \"react-router\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { ConfirmActionDialog } from \"../components/ConfirmActionDialog\";\nimport { useAuthOperations } from \"../hooks/useAuthOperations\";\nimport { useAccountOperations } from \"../hooks/useAccountOperations\";\nimport { formatDate } from \"../utils/formatters\";\nimport { nativeImpact, nativeNotify } from \"../../utils/nativeBridge\";\n\n// Icons\nconst ShieldIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\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);\n\nconst LockIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\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);\n\n// Hidden — moved to Developer Portal\n// const KeyIcon = ({ className }: { className?: string }) => (\n// <svg className={className} fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n// <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={1.5}\n// d=\"M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z\" />\n// </svg>\n// );\n\n// const DelegationIcon = ({ className }: { className?: string }) => (\n// <svg className={className} fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n// <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={1.5}\n// d=\"M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z\" />\n// </svg>\n// );\n\nconst UsersIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z\"\n />\n </svg>\n);\n\nconst UserProfileIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z\"\n />\n </svg>\n);\n\nconst LinkIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1\"\n />\n </svg>\n);\n\nconst ChevronRightIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 5l7 7-7 7\"\n />\n </svg>\n);\n\nconst CheckCircleIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n fill=\"currentColor\"\n viewBox=\"0 0 20 20\"\n aria-hidden=\"true\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z\"\n clipRule=\"evenodd\"\n />\n </svg>\n);\n\nconst ExclamationIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n fill=\"currentColor\"\n viewBox=\"0 0 20 20\"\n aria-hidden=\"true\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z\"\n clipRule=\"evenodd\"\n />\n </svg>\n);\n\nconst SpinnerIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n);\n\ninterface NavItem {\n name: string;\n description: string;\n path: string;\n icon: React.FC<{ className?: string }>;\n color: string;\n bgColor: string;\n adminOnly?: boolean;\n}\n\nconst DELETE_ACCOUNT_CONFIRMATION = \"DELETE MY ACCOUNT\";\n\n/**\n * Security Dashboard - Main hub for all authentication features\n */\nexport function SecurityPage({\n // Admin-only nav cards (Users → `/auth/users`, SSO Configuration →\n // `/auth/sso`) point at platform-global super-admin surfaces whose routes\n // only exist when `enableAdminRoutes={true}` (admin app shell). In\n // user-facing product hosts those routes dead-end on `/auth/login`, so the\n // cards are hidden unless admin routes are enabled. Defaults to false.\n enableAdminRoutes = false,\n}: {\n enableAdminRoutes?: boolean;\n}) {\n const tr = useTr();\n\n const navigate = useNavigate();\n const user = useAuthStore((state) => state.user);\n const { signOut } = useAuthOperations({\n disableCurrentUserQuery: true,\n });\n const { deactivateAccount, deleteAccount, isDeactivating, isDeleting } =\n useAccountOperations();\n const [isLoggingOut, setIsLoggingOut] = useState(false);\n const [showLogoutDialog, setShowLogoutDialog] = useState(false);\n\n const navigationItems: NavItem[] = [\n {\n name: tr(\"auth.security.navProfile\", \"User Profile\"),\n description: tr(\n \"auth.security.navProfileDesc\",\n \"View and edit your profile information\",\n ),\n path: \"/auth/profile\",\n icon: UserProfileIcon,\n color: \"text-accent-teal\",\n bgColor: \"bg-accent-teal-subtle\",\n },\n {\n name: tr(\"auth.security.navMfa\", \"Two-Factor Authentication\"),\n description: tr(\n \"auth.security.navMfaDesc\",\n \"Secure your account with TOTP\",\n ),\n path: \"/auth/mfa\",\n icon: LockIcon,\n color: \"text-accent-purple\",\n bgColor: \"bg-accent-purple-subtle\",\n },\n // Hidden — moved to Developer Portal\n // {\n // name: tr(\"auth.security.navApiKeys\", \"API Keys\"),\n // description: tr(\"auth.security.navApiKeysDesc\", \"Manage programmatic access tokens\"),\n // path: \"/auth/api-keys\",\n // icon: KeyIcon,\n // color: \"text-status-warning-text\",\n // bgColor: \"bg-status-warning-bg-subtle\",\n // },\n // {\n // name: tr(\"auth.security.navDelegations\", \"Delegations\"),\n // description: tr(\"auth.security.navDelegationsDesc\", \"Manage apps acting on your behalf\"),\n // path: \"/auth/delegations\",\n // icon: DelegationIcon,\n // color: \"text-status-warning-text\",\n // bgColor: \"bg-status-warning-bg-subtle\",\n // },\n {\n name: tr(\"auth.security.navUsers\", \"Users\"),\n description: tr(\"auth.security.navUsersDesc\", \"Manage system users\"),\n path: \"/auth/users\",\n icon: UsersIcon,\n color: \"text-accent-indigo\",\n bgColor: \"bg-accent-indigo-subtle\",\n adminOnly: true,\n },\n ];\n\n const quickActions = [\n {\n name: tr(\"auth.security.navSso\", \"SSO Configuration\"),\n description: tr(\"auth.security.navSsoDesc\", \"Enterprise single sign-on\"),\n path: \"/auth/sso\",\n icon: LinkIcon,\n color: \"text-accent-cyan\",\n bgColor: \"bg-accent-cyan-subtle\",\n adminOnly: true,\n },\n ];\n\n // Account management modal states\n const [showDeactivateModal, setShowDeactivateModal] = useState(false);\n const [showDeleteModal, setShowDeleteModal] = useState(false);\n const [accountPassword, setAccountPassword] = useState(\"\");\n const [deleteConfirmPhrase, setDeleteConfirmPhrase] = useState(\"\");\n const [accountError, setAccountError] = useState<string | null>(null);\n const [accountSuccess, setAccountSuccess] = useState<string | null>(null);\n\n // Accessible IDs for modal labels and form fields\n const deactivateTitleId = useId();\n const deactivatePasswordId = useId();\n const deactivateErrorId = useId();\n const deleteTitleId = useId();\n const deletePasswordId = useId();\n const deleteConfirmId = useId();\n const deleteErrorId = useId();\n\n // Focus management: refs to restore focus and manage modal focus\n const deactivateTriggerRef = useRef<HTMLButtonElement>(null);\n const deleteTriggerRef = useRef<HTMLButtonElement>(null);\n const deactivateDialogRef = useRef<HTMLDivElement>(null);\n const deleteDialogRef = useRef<HTMLDivElement>(null);\n\n // Move focus into modal when it opens; restore to trigger on close\n useEffect(() => {\n if (showDeactivateModal) {\n deactivateDialogRef.current?.focus();\n } else {\n deactivateTriggerRef.current?.focus();\n }\n }, [showDeactivateModal]);\n\n useEffect(() => {\n if (showDeleteModal) {\n deleteDialogRef.current?.focus();\n } else {\n deleteTriggerRef.current?.focus();\n }\n }, [showDeleteModal]);\n\n const handleLogout = async () => {\n setIsLoggingOut(true);\n try {\n await signOut();\n navigate(\"/auth/login\");\n setShowLogoutDialog(false);\n } catch (error) {\n console.error(\"Logout failed:\", error);\n setIsLoggingOut(false);\n }\n };\n\n const handleDeactivateAccount = async () => {\n setAccountError(null);\n try {\n const result = await deactivateAccount(accountPassword);\n if (result.success) {\n // Immediately redirect to login page\n navigate(\"/auth/login\", { replace: true });\n } else {\n setAccountError(\n result.message ||\n tr(\n \"auth.security.deactivateFailed\",\n \"Failed to deactivate account\",\n ),\n );\n }\n } catch (error) {\n setAccountError(\n error instanceof Error\n ? error.message\n : tr(\n \"auth.security.deactivateFailed\",\n \"Failed to deactivate account\",\n ),\n );\n }\n };\n\n const handleDeleteAccount = async () => {\n setAccountError(null);\n if (deleteConfirmPhrase !== DELETE_ACCOUNT_CONFIRMATION) {\n setAccountError(\n tr(\n \"auth.security.deleteConfirmMismatch\",\n 'Please type \"DELETE MY ACCOUNT\" to confirm',\n ),\n );\n return;\n }\n void nativeImpact(\"medium\");\n try {\n const result = await deleteAccount(accountPassword, deleteConfirmPhrase);\n if (result.success) {\n void nativeNotify(\"success\");\n // Immediately redirect to login page\n navigate(\"/auth/login\", { replace: true });\n } else {\n void nativeNotify(\"error\");\n setAccountError(\n result.message ||\n tr(\"auth.security.deleteFailed\", \"Failed to delete account\"),\n );\n }\n } catch (error) {\n void nativeNotify(\"error\");\n setAccountError(\n error instanceof Error\n ? error.message\n : tr(\"auth.security.deleteFailed\", \"Failed to delete account\"),\n );\n }\n };\n\n const closeModals = () => {\n setShowDeactivateModal(false);\n setShowDeleteModal(false);\n setAccountPassword(\"\");\n setDeleteConfirmPhrase(\"\");\n setAccountError(null);\n setAccountSuccess(null);\n };\n\n const isAdmin =\n user?.role === \"SUPER_ADMIN\" ||\n user?.role === \"TENANT_ADMIN\" ||\n user?.role === \"ORG_ADMIN\";\n const filteredNavItems = navigationItems.filter(\n (item) => !item.adminOnly || (enableAdminRoutes && isAdmin),\n );\n const filteredQuickActions = quickActions.filter(\n (item) => !item.adminOnly || (enableAdminRoutes && isAdmin),\n );\n\n return (\n <div className=\"bg-bg-canvas p-6\">\n <ConfirmActionDialog\n open={showLogoutDialog}\n onOpenChange={setShowLogoutDialog}\n title={tr(\"auth.security.signOutDialogTitle\", \"Sign out now?\")}\n description={tr(\n \"auth.security.signOutDialogDescription\",\n \"You will be signed out of this device and returned to the login screen.\",\n )}\n confirmText={tr(\"auth.security.signOutDialogConfirm\", \"Sign out\")}\n variant=\"warning\"\n isPending={isLoggingOut}\n onConfirm={handleLogout}\n />\n <main className=\"max-w-7xl mx-auto space-y-8\">\n <div\n className=\"flex items-center gap-3\"\n data-tour=\"auth-security-header\"\n >\n <div className=\"auth-brand-badge flex size-10 items-center justify-center rounded-xl\">\n <ShieldIcon className=\"size-6 text-white\" />\n </div>\n <div>\n <h1 className=\"text-lg font-bold text-text-primary\">\n {tr(\"auth.security.title\", \"Security Center\")}\n </h1>\n <p className=\"text-xs text-text-secondary \">\n {tr(\"auth.security.subtitle\", \"Account & Authentication\")}\n </p>\n </div>\n </div>\n\n <PagePurpose data-tour=\"auth-security-purpose\">\n {tr(\n \"auth.security.purpose\",\n \"Your one place to keep your account safe: turn on two-factor authentication, change your password, review who can act on your behalf, and manage or close your account.\",\n )}\n </PagePurpose>\n\n {/* Security Status Banner — the one emphasis zone on this page */}\n <EmphasisPanel className=\"p-6\" data-tour=\"auth-security-status-banner\">\n <div className=\"flex flex-col md:flex-row md:items-center md:justify-between gap-4\">\n <div className=\"flex items-center gap-4\">\n <div className=\"size-14 rounded-2xl bg-accent-soft flex items-center justify-center\">\n <ShieldIcon className=\"size-8 text-[var(--color-action-primary-bg)]\" />\n </div>\n <div>\n <h2 className=\"text-xl font-bold text-text-primary\">\n {tr(\"auth.security.securityStatus\", \"Security Status\")}\n </h2>\n <p className=\"text-text-secondary\">\n {user?.mfaEnabled\n ? tr(\n \"auth.security.wellProtected\",\n \"Your account is well protected\",\n )\n : tr(\n \"auth.security.enableMfaSuggestion\",\n \"Enable MFA for better security\",\n )}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n {user?.mfaEnabled ? (\n <span className=\"inline-flex items-center gap-2 px-4 py-2 rounded-xl bg-status-success-bg-subtle text-status-success-text font-medium\">\n <CheckCircleIcon className=\"size-5 text-status-success-text\" />\n {tr(\"auth.security.mfaEnabled\", \"MFA Enabled\")}\n </span>\n ) : (\n <Button\n type=\"button\"\n onClick={() => navigate(\"/auth/mfa\")}\n variant=\"default\"\n className=\"font-semibold\"\n >\n <ExclamationIcon className=\"size-5\" />\n {tr(\"auth.security.enableMfaNow\", \"Enable MFA Now\")}\n </Button>\n )}\n </div>\n </div>\n </EmphasisPanel>\n\n {/* Account Information Card */}\n <div\n className=\"mb-8 p-6 rounded-2xl bg-bg-surface border border-border-subtle shadow-sm\"\n data-tour=\"auth-account-info-card\"\n >\n <h3 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr(\"auth.security.accountInfo\", \"Account Information\")}\n </h3>\n <dl className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6\">\n <div className=\"sm:col-span-2 lg:col-span-1\">\n <dt className=\"text-sm font-medium text-text-secondary\">\n {tr(\"auth.security.email\", \"Email\")}\n </dt>\n <dd className=\"mt-1 text-text-primary\">\n <span className=\"block truncate\" title={user?.email}>\n {user?.email}\n </span>\n {user?.emailVerified && (\n <span className=\"mt-1 inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-status-success-bg-subtle text-status-success-text text-xs font-medium\">\n <CheckCircleIcon className=\"size-3\" />\n {tr(\"auth.security.verified\", \"Verified\")}\n </span>\n )}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-text-secondary\">\n {tr(\"auth.security.username\", \"Username\")}\n </dt>\n <dd\n className=\"mt-1 text-text-primary truncate\"\n title={user?.username ?? undefined}\n >\n {user?.username || \"-\"}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-text-secondary\">\n {tr(\"auth.security.role\", \"Role\")}\n </dt>\n <dd className=\"mt-1\">\n <span className=\"inline-flex px-2.5 py-0.5 rounded-full bg-accent-indigo-subtle text-accent-indigo text-xs font-medium\">\n {user?.role || \"USER\"}\n </span>\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-text-secondary\">\n {tr(\"auth.security.memberSince\", \"Member Since\")}\n </dt>\n <dd className=\"mt-1 text-text-primary\">\n {user?.createdAt ? formatDate(user.createdAt) : \"-\"}\n </dd>\n </div>\n </dl>\n </div>\n\n {/* Main Navigation Grid */}\n <div className=\"mb-8\">\n <h3 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr(\"auth.security.securityAuth\", \"Security & Authentication\")}\n </h3>\n <div\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n data-tour=\"auth-security-nav-grid\"\n >\n {filteredNavItems.map((item) => (\n <button\n key={item.path}\n type=\"button\"\n onClick={() => navigate(item.path)}\n aria-label={item.name}\n className=\"group relative p-5 rounded-2xl bg-bg-surface border border-border-subtle hover:border-border-default hover:shadow-lg transition-all duration-200 text-left\"\n >\n <div className=\"flex items-start gap-4\">\n <div\n className={`size-12 rounded-xl ${item.bgColor} flex items-center justify-center flex-shrink-0 transition-transform`}\n >\n <item.icon className={`size-6 ${item.color}`} />\n </div>\n <div className=\"flex-1 min-w-0\">\n <h4 className=\"text-base font-semibold text-text-primary group-hover:text-accent-indigo transition-colors\">\n {item.name}\n </h4>\n <p className=\"mt-1 text-sm text-text-secondary \">\n {item.description}\n </p>\n </div>\n <ChevronRightIcon className=\"size-5 text-text-tertiary group-hover:text-indigo-500 transition-colors flex-shrink-0\" />\n </div>\n </button>\n ))}\n </div>\n </div>\n\n {/* Quick Actions */}\n <div className=\"mb-8\">\n <h3 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr(\"auth.security.quickActions\", \"Quick Actions\")}\n </h3>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n {filteredQuickActions.map((item) => (\n <button\n key={item.path}\n type=\"button\"\n onClick={() => navigate(item.path)}\n aria-label={item.name}\n className=\"group relative p-5 rounded-2xl bg-bg-surface border border-border-subtle hover:border-border-default hover:shadow-lg transition-all duration-200 text-left\"\n >\n <div className=\"flex items-start gap-4\">\n <div\n className={`size-12 rounded-xl ${item.bgColor} flex items-center justify-center flex-shrink-0 transition-transform`}\n >\n <item.icon className={`size-6 ${item.color}`} />\n </div>\n <div className=\"flex-1 min-w-0\">\n <h4 className=\"text-base font-semibold text-text-primary group-hover:text-accent-indigo transition-colors\">\n {item.name}\n </h4>\n <p className=\"mt-1 text-sm text-text-secondary \">\n {item.description}\n </p>\n </div>\n <ChevronRightIcon className=\"size-5 text-text-tertiary group-hover:text-indigo-500 transition-colors flex-shrink-0\" />\n </div>\n </button>\n ))}\n </div>\n </div>\n\n {/* Password & Danger Zone */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Password Section */}\n <div\n className=\"p-6 rounded-2xl bg-bg-surface border border-border-subtle\"\n data-tour=\"auth-password-section\"\n >\n <div className=\"flex items-start justify-between\">\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr(\"auth.security.password\", \"Password\")}\n </h3>\n <p className=\"mt-1 text-sm text-text-secondary \">\n {tr(\n \"auth.security.updatePassword\",\n \"Update your password to keep your account secure\",\n )}\n </p>\n <p className=\"mt-2 text-xs text-text-tertiary \">\n {tr(\"auth.security.lastChanged\", \"Last changed: Never\")}\n </p>\n </div>\n </div>\n <Button\n type=\"button\"\n onClick={() => navigate(\"/auth/reset-password\")}\n variant=\"default\"\n className=\"mt-4 w-full font-semibold sm:w-auto\"\n >\n {tr(\"auth.security.changePassword\", \"Change Password\")}\n </Button>\n </div>\n\n {/* Danger Zone */}\n <div\n className=\"p-6 rounded-2xl bg-status-error-bg-subtle border border-status-error-border\"\n data-tour=\"auth-danger-zone\"\n >\n <h3 className=\"text-lg font-semibold text-status-error-text\">\n {tr(\"auth.security.dangerZone\", \"Danger Zone\")}\n </h3>\n <p className=\"mt-1 text-sm text-status-error-text/80\">\n {tr(\n \"auth.security.dangerWarning\",\n \"These actions affect your account. Proceed with caution.\",\n )}\n </p>\n <div className=\"mt-4 flex flex-wrap gap-3\">\n <button\n ref={deactivateTriggerRef}\n type=\"button\"\n onClick={() => setShowDeactivateModal(true)}\n className=\"px-5 py-2.5 rounded-xl border-2 border-status-warning-border text-status-warning-text font-semibold hover:bg-status-warning-bg-subtle transition-all duration-200\"\n >\n {tr(\"auth.security.deactivateAccount\", \"Deactivate Account\")}\n </button>\n <button\n ref={deleteTriggerRef}\n type=\"button\"\n onClick={() => setShowDeleteModal(true)}\n className=\"px-5 py-2.5 rounded-xl border-2 border-status-error-border text-status-error-text font-semibold hover:bg-status-error-bg-subtle transition-all duration-200\"\n >\n {tr(\"auth.security.deleteAccount\", \"Delete Account\")}\n </button>\n </div>\n </div>\n </div>\n\n {/* Deactivate Account Modal */}\n {showDeactivateModal && (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4\"\n role=\"presentation\"\n onClick={(e) => {\n if (e.target === e.currentTarget) closeModals();\n }}\n >\n <div\n ref={deactivateDialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={deactivateTitleId}\n tabIndex={-1}\n className=\"w-full max-w-md bg-bg-surface rounded-2xl shadow-2xl p-6 outline-none\"\n >\n <h3\n id={deactivateTitleId}\n className=\"text-xl font-bold text-text-primary mb-2\"\n >\n {tr(\"auth.security.deactivateTitle\", \"Deactivate Account\")}\n </h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n {tr(\n \"auth.security.deactivateDesc\",\n \"Your account will be temporarily disabled. You can reactivate it by signing in again with your credentials.\",\n )}\n </p>\n {accountError && (\n <div\n id={deactivateErrorId}\n role=\"alert\"\n className=\"mb-4 p-3 rounded-lg bg-status-error-bg-subtle text-status-error-text text-sm\"\n >\n {accountError}\n </div>\n )}\n {accountSuccess && (\n <output className=\"block mb-4 p-3 rounded-lg bg-status-success-bg-subtle text-status-success-text text-sm\">\n {accountSuccess}\n </output>\n )}\n <div className=\"mb-4\">\n <label\n htmlFor={deactivatePasswordId}\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr(\n \"auth.security.enterPasswordConfirm\",\n \"Enter your password to confirm\",\n )}\n </label>\n <input\n id={deactivatePasswordId}\n type=\"password\"\n value={accountPassword}\n onChange={(e) => setAccountPassword(e.target.value)}\n aria-required=\"true\"\n aria-label={tr(\n \"auth.security.enterPasswordConfirm\",\n \"Enter your password to confirm\",\n )}\n aria-describedby={\n accountError ? deactivateErrorId : undefined\n }\n aria-invalid={!!accountError}\n autoComplete=\"current-password\"\n className=\"w-full px-4 py-2 rounded-lg border border-border-default bg-bg-surface text-text-primary focus:ring-2 focus:ring-status-warning-bg focus:border-transparent\"\n placeholder={tr(\n \"auth.security.yourPassword\",\n \"Your password\",\n )}\n />\n </div>\n <div className=\"flex gap-3\">\n <button\n type=\"button\"\n onClick={closeModals}\n className=\"flex-1 px-4 py-2 rounded-lg border border-border-default text-text-primary font-medium hover:bg-bg-sunken transition-colors\"\n >\n {tr(\"auth.security.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleDeactivateAccount()}\n disabled={!accountPassword || isDeactivating}\n className=\"flex-1 px-4 py-2 rounded-lg bg-status-warning-bg text-white font-medium hover:bg-status-warning-bg-hover disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center justify-center gap-2\"\n >\n {isDeactivating && (\n <SpinnerIcon className=\"size-4 animate-spin\" />\n )}\n {tr(\"auth.security.deactivateBtn\", \"Deactivate\")}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Delete Account Modal */}\n {showDeleteModal && (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4\"\n role=\"presentation\"\n onClick={(e) => {\n if (e.target === e.currentTarget) closeModals();\n }}\n >\n <div\n ref={deleteDialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={deleteTitleId}\n tabIndex={-1}\n className=\"w-full max-w-md bg-bg-surface rounded-2xl shadow-2xl p-6 outline-none\"\n >\n <h3\n id={deleteTitleId}\n className=\"text-xl font-bold text-status-error-text mb-2\"\n >\n {tr(\"auth.security.deleteTitle\", \"Delete Account Permanently\")}\n </h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n {tr(\n \"auth.security.deleteDesc\",\n \"This action is irreversible. All your data will be permanently deleted and cannot be recovered.\",\n )}\n </p>\n {accountError && (\n <div\n id={deleteErrorId}\n role=\"alert\"\n className=\"mb-4 p-3 rounded-lg bg-status-error-bg-subtle text-status-error-text text-sm\"\n >\n {accountError}\n </div>\n )}\n {accountSuccess && (\n <output className=\"block mb-4 p-3 rounded-lg bg-status-success-bg-subtle text-status-success-text text-sm\">\n {accountSuccess}\n </output>\n )}\n <div className=\"mb-4\">\n <label\n htmlFor={deletePasswordId}\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr(\n \"auth.security.enterPasswordDelete\",\n \"Enter your password\",\n )}\n </label>\n <input\n id={deletePasswordId}\n type=\"password\"\n value={accountPassword}\n onChange={(e) => setAccountPassword(e.target.value)}\n aria-required=\"true\"\n aria-label={tr(\n \"auth.security.enterPasswordDelete\",\n \"Enter your password\",\n )}\n aria-describedby={accountError ? deleteErrorId : undefined}\n aria-invalid={!!accountError}\n autoComplete=\"current-password\"\n className=\"w-full px-4 py-2 rounded-lg border border-border-default bg-bg-surface text-text-primary focus:ring-2 focus:ring-status-error-bg focus:border-transparent\"\n placeholder={tr(\n \"auth.security.yourPassword\",\n \"Your password\",\n )}\n />\n </div>\n <div className=\"mb-4\">\n <label\n htmlFor={deleteConfirmId}\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr(\"auth.security.type\", \"Type\")}{\" \"}\n <code className=\"px-1 py-0.5 rounded bg-bg-sunken text-status-error-text font-mono text-sm\">\n {DELETE_ACCOUNT_CONFIRMATION}\n </code>{\" \"}\n {tr(\"auth.security.toConfirm\", \"to confirm\")}\n </label>\n <input\n id={deleteConfirmId}\n type=\"text\"\n value={deleteConfirmPhrase}\n onChange={(e) => setDeleteConfirmPhrase(e.target.value)}\n aria-required=\"true\"\n aria-label={tr(\n \"auth.security.deleteConfirmAriaLabel\",\n \"Type DELETE MY ACCOUNT to confirm\",\n )}\n aria-describedby={accountError ? deleteErrorId : undefined}\n aria-invalid={\n deleteConfirmPhrase.length > 0 &&\n deleteConfirmPhrase !== DELETE_ACCOUNT_CONFIRMATION\n }\n autoComplete=\"off\"\n className=\"w-full px-4 py-2 rounded-lg border border-border-default bg-bg-surface text-text-primary focus:ring-2 focus:ring-status-error-bg focus:border-transparent font-mono\"\n placeholder={tr(\n \"auth.security.deleteConfirmPlaceholder\",\n \"DELETE MY ACCOUNT\",\n )}\n />\n </div>\n <div className=\"flex gap-3\">\n <button\n type=\"button\"\n onClick={closeModals}\n className=\"flex-1 px-4 py-2 rounded-lg border border-border-default text-text-primary font-medium hover:bg-bg-sunken transition-colors\"\n >\n {tr(\"auth.security.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleDeleteAccount()}\n disabled={\n !accountPassword ||\n deleteConfirmPhrase !== DELETE_ACCOUNT_CONFIRMATION ||\n isDeleting\n }\n className=\"flex-1 px-4 py-2 rounded-lg bg-status-error-bg text-white font-medium hover:bg-status-error-bg-hover disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center justify-center gap-2\"\n >\n {isDeleting && (\n <SpinnerIcon className=\"size-4 animate-spin\" />\n )}\n {tr(\"auth.security.deleteForever\", \"Delete Forever\")}\n </button>\n </div>\n </div>\n </div>\n )}\n\n <NextSteps\n storageKey=\"auth-security\"\n steps={[\n ...(user?.mfaEnabled\n ? []\n : [\n {\n id: \"enable-mfa\",\n label: tr(\n \"auth.security.nextEnableMfa\",\n \"Turn on two-factor authentication\",\n ),\n description: tr(\n \"auth.security.nextEnableMfaDesc\",\n \"The single biggest step you can take to protect your account.\",\n ),\n onClick: () => navigate(\"/auth/mfa\"),\n },\n ]),\n {\n id: \"review-profile\",\n label: tr(\n \"auth.security.nextProfile\",\n \"Review your profile details\",\n ),\n description: tr(\n \"auth.security.nextProfileDesc\",\n \"Make sure your name, email, and contact info are current.\",\n ),\n onClick: () => navigate(\"/auth/profile\"),\n },\n {\n id: \"change-password\",\n label: tr(\"auth.security.nextPassword\", \"Update your password\"),\n description: tr(\n \"auth.security.nextPasswordDesc\",\n \"Set a strong, unique password if you haven't recently.\",\n ),\n onClick: () => navigate(\"/auth/reset-password\"),\n },\n ]}\n />\n\n {/* Footer */}\n <footer className=\"mt-12 pt-6 border-t border-border-subtle \">\n <div\n className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 text-sm text-text-secondary \"\n data-tour=\"auth-footer-actions\"\n >\n <p>\n {tr(\"auth.security.signedInAs\", \"Signed in as\")}{\" \"}\n <span className=\"font-medium text-text-primary \">\n {user?.email}\n </span>\n </p>\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/login\")}\n className=\"hover:text-text-primary transition-colors\"\n >\n {tr(\"auth.security.switchAccount\", \"Switch Account\")}\n </button>\n <span className=\"text-text-muted\" aria-hidden=\"true\">\n |\n </span>\n <button\n type=\"button\"\n onClick={() => setShowLogoutDialog(true)}\n className=\"hover:text-status-error-text transition-colors\"\n >\n {tr(\"auth.security.signOut\", \"Sign Out\")}\n </button>\n </div>\n </div>\n </footer>\n </main>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SecurityPage.js","names":["ShieldIcon","LockIcon","UsersIcon","UserProfileIcon","LinkIcon","ChevronRightIcon","CheckCircleIcon","ExclamationIcon","SpinnerIcon","DELETE_ACCOUNT_CONFIRMATION","SecurityPage","tr","useTr","navigate","useNavigate","user","useAuthStore","state","useAuthOperations","useAccountOperations","isLoggingOut","setIsLoggingOut","useState","showLogoutDialog","setShowLogoutDialog","navigationItems","quickActions","showDeactivateModal","setShowDeactivateModal","showDeleteModal","setShowDeleteModal","accountPassword","setAccountPassword","deleteConfirmPhrase","setDeleteConfirmPhrase","accountError","setAccountError","accountSuccess","setAccountSuccess","deactivateTitleId","useId","deactivatePasswordId","deactivateErrorId","deleteTitleId","deletePasswordId","deleteConfirmId","deleteErrorId","deactivateTriggerRef","useRef","deleteTriggerRef","deactivateDialogRef","deleteDialogRef","useEffect","handleLogout","signOut","error","handleDeactivateAccount","result","deactivateAccount","handleDeleteAccount","deleteAccount","nativeNotify","closeModals","isAdmin","filteredNavItems","item","enableAdminRoutes","filteredQuickActions","ConfirmActionDialog","PagePurpose","EmphasisPanel","Button","formatDate","isDeactivating","isDeleting","NextSteps"],"sources":["../../../src/auth/pages/SecurityPage.tsx"],"sourcesContent":["import { useId, useRef, useEffect, useState } from \"react\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport { Button } from \"@burdenoff/fe-libs/button\";\nimport { EmphasisPanel, PagePurpose, NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport { useNavigate } from \"react-router\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { ConfirmActionDialog } from \"../components/ConfirmActionDialog\";\nimport { useAuthOperations } from \"../hooks/useAuthOperations\";\nimport { useAccountOperations } from \"../hooks/useAccountOperations\";\nimport { formatDate } from \"../utils/formatters\";\nimport { nativeImpact, nativeNotify } from \"../../utils/nativeBridge\";\n\n// Icons\nconst ShieldIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\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);\n\nconst LockIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\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);\n\n// Hidden — moved to Developer Portal\n// const KeyIcon = ({ className }: { className?: string }) => (\n// <svg className={className} fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n// <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={1.5}\n// d=\"M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z\" />\n// </svg>\n// );\n\n// const DelegationIcon = ({ className }: { className?: string }) => (\n// <svg className={className} fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n// <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={1.5}\n// d=\"M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z\" />\n// </svg>\n// );\n\nconst UsersIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z\"\n />\n </svg>\n);\n\nconst UserProfileIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z\"\n />\n </svg>\n);\n\nconst LinkIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1\"\n />\n </svg>\n);\n\nconst ChevronRightIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 5l7 7-7 7\"\n />\n </svg>\n);\n\nconst CheckCircleIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n fill=\"currentColor\"\n viewBox=\"0 0 20 20\"\n aria-hidden=\"true\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z\"\n clipRule=\"evenodd\"\n />\n </svg>\n);\n\nconst ExclamationIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n fill=\"currentColor\"\n viewBox=\"0 0 20 20\"\n aria-hidden=\"true\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z\"\n clipRule=\"evenodd\"\n />\n </svg>\n);\n\nconst SpinnerIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n);\n\ninterface NavItem {\n name: string;\n description: string;\n path: string;\n icon: React.FC<{ className?: string }>;\n color: string;\n bgColor: string;\n adminOnly?: boolean;\n}\n\nconst DELETE_ACCOUNT_CONFIRMATION = \"DELETE MY ACCOUNT\";\n\n/**\n * Security Dashboard - Main hub for all authentication features\n */\nexport function SecurityPage({\n // Admin-only nav cards (Users → `/auth/users`, SSO Configuration →\n // `/auth/sso`) point at platform-global super-admin surfaces whose routes\n // only exist when `enableAdminRoutes={true}` (admin app shell). In\n // user-facing product hosts those routes dead-end on `/auth/login`, so the\n // cards are hidden unless admin routes are enabled. Defaults to false.\n enableAdminRoutes = false,\n}: {\n enableAdminRoutes?: boolean;\n}) {\n const tr = useTr();\n\n const navigate = useNavigate();\n const user = useAuthStore((state) => state.user);\n const { signOut } = useAuthOperations({\n disableCurrentUserQuery: true,\n });\n const { deactivateAccount, deleteAccount, isDeactivating, isDeleting } =\n useAccountOperations();\n const [isLoggingOut, setIsLoggingOut] = useState(false);\n const [showLogoutDialog, setShowLogoutDialog] = useState(false);\n\n const navigationItems: NavItem[] = [\n {\n name: tr(\"auth.security.navProfile\", \"User Profile\"),\n description: tr(\n \"auth.security.navProfileDesc\",\n \"View and edit your profile information\",\n ),\n path: \"/auth/profile\",\n icon: UserProfileIcon,\n color: \"text-accent-teal\",\n bgColor: \"bg-accent-teal-subtle\",\n },\n {\n name: tr(\"auth.security.navMfa\", \"Two-Factor Authentication\"),\n description: tr(\n \"auth.security.navMfaDesc\",\n \"Secure your account with TOTP\",\n ),\n path: \"/auth/mfa\",\n icon: LockIcon,\n color: \"text-accent-purple\",\n bgColor: \"bg-accent-purple-subtle\",\n },\n // Hidden — moved to Developer Portal\n // {\n // name: tr(\"auth.security.navApiKeys\", \"API Keys\"),\n // description: tr(\"auth.security.navApiKeysDesc\", \"Manage programmatic access tokens\"),\n // path: \"/auth/api-keys\",\n // icon: KeyIcon,\n // color: \"text-status-warning-text\",\n // bgColor: \"bg-status-warning-bg-subtle\",\n // },\n // {\n // name: tr(\"auth.security.navDelegations\", \"Delegations\"),\n // description: tr(\"auth.security.navDelegationsDesc\", \"Manage apps acting on your behalf\"),\n // path: \"/auth/delegations\",\n // icon: DelegationIcon,\n // color: \"text-status-warning-text\",\n // bgColor: \"bg-status-warning-bg-subtle\",\n // },\n {\n name: tr(\"auth.security.navUsers\", \"Users\"),\n description: tr(\"auth.security.navUsersDesc\", \"Manage system users\"),\n path: \"/auth/users\",\n icon: UsersIcon,\n color: \"text-accent-indigo\",\n bgColor: \"bg-accent-indigo-subtle\",\n adminOnly: true,\n },\n ];\n\n const quickActions = [\n {\n name: tr(\"auth.security.navSso\", \"SSO Configuration\"),\n description: tr(\"auth.security.navSsoDesc\", \"Enterprise single sign-on\"),\n path: \"/auth/sso\",\n icon: LinkIcon,\n color: \"text-accent-cyan\",\n bgColor: \"bg-accent-cyan-subtle\",\n adminOnly: true,\n },\n ];\n\n // Account management modal states\n const [showDeactivateModal, setShowDeactivateModal] = useState(false);\n const [showDeleteModal, setShowDeleteModal] = useState(false);\n const [accountPassword, setAccountPassword] = useState(\"\");\n const [deleteConfirmPhrase, setDeleteConfirmPhrase] = useState(\"\");\n const [accountError, setAccountError] = useState<string | null>(null);\n const [accountSuccess, setAccountSuccess] = useState<string | null>(null);\n\n // Accessible IDs for modal labels and form fields\n const deactivateTitleId = useId();\n const deactivatePasswordId = useId();\n const deactivateErrorId = useId();\n const deleteTitleId = useId();\n const deletePasswordId = useId();\n const deleteConfirmId = useId();\n const deleteErrorId = useId();\n\n // Focus management: refs to restore focus and manage modal focus\n const deactivateTriggerRef = useRef<HTMLButtonElement>(null);\n const deleteTriggerRef = useRef<HTMLButtonElement>(null);\n const deactivateDialogRef = useRef<HTMLDivElement>(null);\n const deleteDialogRef = useRef<HTMLDivElement>(null);\n\n // Move focus into modal when it opens; restore to trigger on close\n useEffect(() => {\n if (showDeactivateModal) {\n deactivateDialogRef.current?.focus();\n } else {\n deactivateTriggerRef.current?.focus();\n }\n }, [showDeactivateModal]);\n\n useEffect(() => {\n if (showDeleteModal) {\n deleteDialogRef.current?.focus();\n } else {\n deleteTriggerRef.current?.focus();\n }\n }, [showDeleteModal]);\n\n const handleLogout = async () => {\n setIsLoggingOut(true);\n try {\n await signOut();\n navigate(\"/auth/login\");\n setShowLogoutDialog(false);\n } catch (error) {\n console.error(\"Logout failed:\", error);\n setIsLoggingOut(false);\n }\n };\n\n const handleDeactivateAccount = async () => {\n setAccountError(null);\n try {\n const result = await deactivateAccount(accountPassword);\n if (result.success) {\n // Immediately redirect to login page\n navigate(\"/auth/login\", { replace: true });\n } else {\n setAccountError(\n result.message ||\n tr(\n \"auth.security.deactivateFailed\",\n \"Failed to deactivate account\",\n ),\n );\n }\n } catch (error) {\n setAccountError(\n error instanceof Error\n ? error.message\n : tr(\n \"auth.security.deactivateFailed\",\n \"Failed to deactivate account\",\n ),\n );\n }\n };\n\n const handleDeleteAccount = async () => {\n setAccountError(null);\n if (deleteConfirmPhrase !== DELETE_ACCOUNT_CONFIRMATION) {\n setAccountError(\n tr(\n \"auth.security.deleteConfirmMismatch\",\n 'Please type \"DELETE MY ACCOUNT\" to confirm',\n ),\n );\n return;\n }\n void nativeImpact(\"medium\");\n try {\n const result = await deleteAccount(accountPassword, deleteConfirmPhrase);\n if (result.success) {\n void nativeNotify(\"success\");\n // Immediately redirect to login page\n navigate(\"/auth/login\", { replace: true });\n } else {\n void nativeNotify(\"error\");\n setAccountError(\n result.message ||\n tr(\"auth.security.deleteFailed\", \"Failed to delete account\"),\n );\n }\n } catch (error) {\n void nativeNotify(\"error\");\n setAccountError(\n error instanceof Error\n ? error.message\n : tr(\"auth.security.deleteFailed\", \"Failed to delete account\"),\n );\n }\n };\n\n const closeModals = () => {\n setShowDeactivateModal(false);\n setShowDeleteModal(false);\n setAccountPassword(\"\");\n setDeleteConfirmPhrase(\"\");\n setAccountError(null);\n setAccountSuccess(null);\n };\n\n const isAdmin =\n user?.role === \"SUPER_ADMIN\" ||\n user?.role === \"TENANT_ADMIN\" ||\n user?.role === \"ORG_ADMIN\";\n const filteredNavItems = navigationItems.filter(\n (item) => !item.adminOnly || (enableAdminRoutes && isAdmin),\n );\n const filteredQuickActions = quickActions.filter(\n (item) => !item.adminOnly || (enableAdminRoutes && isAdmin),\n );\n\n return (\n <div className=\"bg-bg-canvas p-6\">\n <ConfirmActionDialog\n open={showLogoutDialog}\n onOpenChange={setShowLogoutDialog}\n title={tr(\"auth.security.signOutDialogTitle\", \"Sign out now?\")}\n description={tr(\n \"auth.security.signOutDialogDescription\",\n \"You will be signed out of this device and returned to the login screen.\",\n )}\n confirmText={tr(\"auth.security.signOutDialogConfirm\", \"Sign out\")}\n variant=\"warning\"\n isPending={isLoggingOut}\n onConfirm={handleLogout}\n />\n <main className=\"max-w-7xl mx-auto space-y-8\">\n <div\n className=\"flex items-center gap-3\"\n data-tour=\"auth-security-header\"\n >\n <div className=\"auth-brand-badge flex size-10 items-center justify-center rounded-xl\">\n <ShieldIcon className=\"size-6 text-white\" />\n </div>\n <div>\n <h1 className=\"text-lg font-bold text-text-primary\">\n {tr(\"auth.security.title\", \"Security Center\")}\n </h1>\n <p className=\"text-xs text-text-secondary \">\n {tr(\"auth.security.subtitle\", \"Account & Authentication\")}\n </p>\n </div>\n </div>\n\n <PagePurpose data-tour=\"auth-security-purpose\">\n {tr(\n \"auth.security.purpose\",\n \"Your one place to keep your account safe: turn on two-factor authentication, change your password, review who can act on your behalf, and manage or close your account.\",\n )}\n </PagePurpose>\n\n {/* Security Status Banner — the one emphasis zone on this page */}\n <EmphasisPanel className=\"p-6\" data-tour=\"auth-security-status-banner\">\n <div className=\"flex flex-col md:flex-row md:items-center md:justify-between gap-4\">\n <div className=\"flex items-center gap-4\">\n <div className=\"size-14 rounded-2xl bg-accent-soft flex items-center justify-center\">\n <ShieldIcon className=\"size-8 text-[var(--color-action-primary-bg)]\" />\n </div>\n <div>\n <h2 className=\"text-xl font-bold text-text-primary\">\n {tr(\"auth.security.securityStatus\", \"Security Status\")}\n </h2>\n <p className=\"text-text-secondary\">\n {user?.mfaEnabled\n ? tr(\n \"auth.security.wellProtected\",\n \"Your account is well protected\",\n )\n : tr(\n \"auth.security.enableMfaSuggestion\",\n \"Enable MFA for better security\",\n )}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-3\">\n {user?.mfaEnabled ? (\n <span className=\"inline-flex items-center gap-2 px-4 py-2 rounded-xl bg-status-success-bg-subtle text-status-success-text font-medium\">\n <CheckCircleIcon className=\"size-5 text-status-success-text\" />\n {tr(\"auth.security.mfaEnabled\", \"MFA Enabled\")}\n </span>\n ) : (\n <Button\n type=\"button\"\n onClick={() => navigate(\"/auth/mfa\")}\n variant=\"default\"\n className=\"font-semibold\"\n >\n <ExclamationIcon className=\"size-5\" />\n {tr(\"auth.security.enableMfaNow\", \"Enable MFA Now\")}\n </Button>\n )}\n </div>\n </div>\n </EmphasisPanel>\n\n {/* Account Information Card */}\n <div\n className=\"mb-8 p-6 rounded-2xl bg-bg-surface border border-border-subtle shadow-sm\"\n data-tour=\"auth-account-info-card\"\n >\n <h3 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr(\"auth.security.accountInfo\", \"Account Information\")}\n </h3>\n <dl className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6\">\n <div className=\"sm:col-span-2 lg:col-span-1\">\n <dt className=\"text-sm font-medium text-text-secondary\">\n {tr(\"auth.security.email\", \"Email\")}\n </dt>\n <dd className=\"mt-1 text-text-primary\">\n <span className=\"block truncate\" title={user?.email}>\n {user?.email}\n </span>\n {user?.emailVerified && (\n <span className=\"mt-1 inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-status-success-bg-subtle text-status-success-text text-xs font-medium\">\n <CheckCircleIcon className=\"size-3\" />\n {tr(\"auth.security.verified\", \"Verified\")}\n </span>\n )}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-text-secondary\">\n {tr(\"auth.security.username\", \"Username\")}\n </dt>\n <dd\n className=\"mt-1 text-text-primary truncate\"\n title={user?.username ?? undefined}\n >\n {user?.username || \"-\"}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-text-secondary\">\n {tr(\"auth.security.role\", \"Role\")}\n </dt>\n <dd className=\"mt-1\">\n <span className=\"inline-flex px-2.5 py-0.5 rounded-full bg-accent-indigo-subtle text-accent-indigo text-xs font-medium\">\n {user?.role || \"USER\"}\n </span>\n </dd>\n </div>\n <div>\n <dt className=\"text-sm font-medium text-text-secondary\">\n {tr(\"auth.security.memberSince\", \"Member Since\")}\n </dt>\n <dd className=\"mt-1 text-text-primary\">\n {user?.createdAt ? formatDate(user.createdAt) : \"-\"}\n </dd>\n </div>\n </dl>\n </div>\n\n {/* Main Navigation Grid */}\n <div className=\"mb-8\">\n <h3 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr(\"auth.security.securityAuth\", \"Security & Authentication\")}\n </h3>\n <div\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n data-tour=\"auth-security-nav-grid\"\n >\n {filteredNavItems.map((item) => (\n <button\n key={item.path}\n type=\"button\"\n onClick={() => navigate(item.path)}\n aria-label={item.name}\n className=\"group relative p-5 rounded-2xl bg-bg-surface border border-border-subtle hover:border-border-default hover:shadow-lg transition-all duration-200 text-left\"\n >\n <div className=\"flex items-start gap-4\">\n <div\n className={`size-12 rounded-xl ${item.bgColor} flex items-center justify-center flex-shrink-0 transition-transform`}\n >\n <item.icon className={`size-6 ${item.color}`} />\n </div>\n <div className=\"flex-1 min-w-0\">\n <h4 className=\"text-base font-semibold text-text-primary group-hover:text-accent-indigo transition-colors\">\n {item.name}\n </h4>\n <p className=\"mt-1 text-sm text-text-secondary \">\n {item.description}\n </p>\n </div>\n <ChevronRightIcon className=\"size-5 text-text-tertiary group-hover:text-indigo-500 transition-colors flex-shrink-0\" />\n </div>\n </button>\n ))}\n </div>\n </div>\n\n {/* Quick Actions */}\n <div className=\"mb-8\">\n <h3 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr(\"auth.security.quickActions\", \"Quick Actions\")}\n </h3>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n {filteredQuickActions.map((item) => (\n <button\n key={item.path}\n type=\"button\"\n onClick={() => navigate(item.path)}\n aria-label={item.name}\n className=\"group relative p-5 rounded-2xl bg-bg-surface border border-border-subtle hover:border-border-default hover:shadow-lg transition-all duration-200 text-left\"\n >\n <div className=\"flex items-start gap-4\">\n <div\n className={`size-12 rounded-xl ${item.bgColor} flex items-center justify-center flex-shrink-0 transition-transform`}\n >\n <item.icon className={`size-6 ${item.color}`} />\n </div>\n <div className=\"flex-1 min-w-0\">\n <h4 className=\"text-base font-semibold text-text-primary group-hover:text-accent-indigo transition-colors\">\n {item.name}\n </h4>\n <p className=\"mt-1 text-sm text-text-secondary \">\n {item.description}\n </p>\n </div>\n <ChevronRightIcon className=\"size-5 text-text-tertiary group-hover:text-indigo-500 transition-colors flex-shrink-0\" />\n </div>\n </button>\n ))}\n </div>\n </div>\n\n {/* Password & Danger Zone */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Password Section */}\n <div\n className=\"p-6 rounded-2xl bg-bg-surface border border-border-subtle\"\n data-tour=\"auth-password-section\"\n >\n <div className=\"flex items-start justify-between\">\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary\">\n {tr(\"auth.security.password\", \"Password\")}\n </h3>\n <p className=\"mt-1 text-sm text-text-secondary \">\n {tr(\n \"auth.security.updatePassword\",\n \"Update your password to keep your account secure\",\n )}\n </p>\n <p className=\"mt-2 text-xs text-text-tertiary \">\n {tr(\"auth.security.lastChanged\", \"Last changed: Never\")}\n </p>\n </div>\n </div>\n <Button\n type=\"button\"\n onClick={() => navigate(\"/auth/reset-password\")}\n variant=\"default\"\n className=\"mt-4 w-full font-semibold sm:w-auto\"\n >\n {tr(\"auth.security.changePassword\", \"Change Password\")}\n </Button>\n </div>\n\n {/* Danger Zone */}\n <div\n className=\"p-6 rounded-2xl bg-status-error-bg-subtle border border-status-error-border\"\n data-tour=\"auth-danger-zone\"\n >\n <h3 className=\"text-lg font-semibold text-status-error-text\">\n {tr(\"auth.security.dangerZone\", \"Danger Zone\")}\n </h3>\n <p className=\"mt-1 text-sm text-status-error-text/80\">\n {tr(\n \"auth.security.dangerWarning\",\n \"These actions affect your account. Proceed with caution.\",\n )}\n </p>\n <div className=\"mt-4 flex flex-wrap gap-3\">\n <button\n ref={deactivateTriggerRef}\n type=\"button\"\n onClick={() => setShowDeactivateModal(true)}\n className=\"px-5 py-2.5 rounded-xl border-2 border-status-warning-border text-status-warning-text font-semibold hover:bg-status-warning-bg-subtle transition-all duration-200\"\n >\n {tr(\"auth.security.deactivateAccount\", \"Deactivate Account\")}\n </button>\n <button\n ref={deleteTriggerRef}\n type=\"button\"\n onClick={() => setShowDeleteModal(true)}\n className=\"px-5 py-2.5 rounded-xl border-2 border-status-error-border text-status-error-text font-semibold hover:bg-status-error-bg-subtle transition-all duration-200\"\n >\n {tr(\"auth.security.deleteAccount\", \"Delete Account\")}\n </button>\n </div>\n </div>\n </div>\n\n {/* Deactivate Account Modal */}\n {showDeactivateModal && (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4\"\n role=\"presentation\"\n onClick={(e) => {\n if (e.target === e.currentTarget) closeModals();\n }}\n >\n <div\n ref={deactivateDialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={deactivateTitleId}\n tabIndex={-1}\n className=\"w-full max-w-md bg-bg-surface rounded-2xl shadow-2xl p-6 outline-none\"\n >\n <h3\n id={deactivateTitleId}\n className=\"text-xl font-bold text-text-primary mb-2\"\n >\n {tr(\"auth.security.deactivateTitle\", \"Deactivate Account\")}\n </h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n {tr(\n \"auth.security.deactivateDesc\",\n \"Your account will be temporarily disabled. You can reactivate it by signing in again with your credentials.\",\n )}\n </p>\n {accountError && (\n <div\n id={deactivateErrorId}\n role=\"alert\"\n className=\"mb-4 p-3 rounded-lg bg-status-error-bg-subtle text-status-error-text text-sm\"\n >\n {accountError}\n </div>\n )}\n {accountSuccess && (\n <output className=\"block mb-4 p-3 rounded-lg bg-status-success-bg-subtle text-status-success-text text-sm\">\n {accountSuccess}\n </output>\n )}\n <div className=\"mb-4\">\n <label\n htmlFor={deactivatePasswordId}\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr(\n \"auth.security.enterPasswordConfirm\",\n \"Enter your password to confirm\",\n )}\n </label>\n <input\n id={deactivatePasswordId}\n type=\"password\"\n value={accountPassword}\n onChange={(e) => setAccountPassword(e.target.value)}\n aria-required=\"true\"\n aria-label={tr(\n \"auth.security.enterPasswordConfirm\",\n \"Enter your password to confirm\",\n )}\n aria-describedby={\n accountError ? deactivateErrorId : undefined\n }\n aria-invalid={!!accountError}\n autoComplete=\"current-password\"\n className=\"w-full px-4 py-2 rounded-lg border border-border-default bg-bg-surface text-text-primary focus:ring-2 focus:ring-status-warning-bg focus:border-transparent\"\n placeholder={tr(\n \"auth.security.yourPassword\",\n \"Your password\",\n )}\n />\n </div>\n <div className=\"flex gap-3\">\n <button\n type=\"button\"\n onClick={closeModals}\n className=\"flex-1 px-4 py-2 rounded-lg border border-border-default text-text-primary font-medium hover:bg-bg-sunken transition-colors\"\n >\n {tr(\"auth.security.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleDeactivateAccount()}\n disabled={!accountPassword || isDeactivating}\n className=\"flex-1 px-4 py-2 rounded-lg bg-status-warning-bg text-white font-medium hover:bg-status-warning-bg-hover disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center justify-center gap-2\"\n >\n {isDeactivating && (\n <SpinnerIcon className=\"size-4 animate-spin\" />\n )}\n {tr(\"auth.security.deactivateBtn\", \"Deactivate\")}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Delete Account Modal */}\n {showDeleteModal && (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4\"\n role=\"presentation\"\n onClick={(e) => {\n if (e.target === e.currentTarget) closeModals();\n }}\n >\n <div\n ref={deleteDialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={deleteTitleId}\n tabIndex={-1}\n className=\"w-full max-w-md bg-bg-surface rounded-2xl shadow-2xl p-6 outline-none\"\n >\n <h3\n id={deleteTitleId}\n className=\"text-xl font-bold text-status-error-text mb-2\"\n >\n {tr(\"auth.security.deleteTitle\", \"Delete Account Permanently\")}\n </h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n {tr(\n \"auth.security.deleteDesc\",\n \"This action is irreversible. All your data will be permanently deleted and cannot be recovered.\",\n )}\n </p>\n {accountError && (\n <div\n id={deleteErrorId}\n role=\"alert\"\n className=\"mb-4 p-3 rounded-lg bg-status-error-bg-subtle text-status-error-text text-sm\"\n >\n {accountError}\n </div>\n )}\n {accountSuccess && (\n <output className=\"block mb-4 p-3 rounded-lg bg-status-success-bg-subtle text-status-success-text text-sm\">\n {accountSuccess}\n </output>\n )}\n <div className=\"mb-4\">\n <label\n htmlFor={deletePasswordId}\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr(\n \"auth.security.enterPasswordDelete\",\n \"Enter your password\",\n )}\n </label>\n <input\n id={deletePasswordId}\n type=\"password\"\n value={accountPassword}\n onChange={(e) => setAccountPassword(e.target.value)}\n aria-required=\"true\"\n aria-label={tr(\n \"auth.security.enterPasswordDelete\",\n \"Enter your password\",\n )}\n aria-describedby={accountError ? deleteErrorId : undefined}\n aria-invalid={!!accountError}\n autoComplete=\"current-password\"\n className=\"w-full px-4 py-2 rounded-lg border border-border-default bg-bg-surface text-text-primary focus:ring-2 focus:ring-status-error-bg focus:border-transparent\"\n placeholder={tr(\n \"auth.security.yourPassword\",\n \"Your password\",\n )}\n />\n </div>\n <div className=\"mb-4\">\n <label\n htmlFor={deleteConfirmId}\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr(\"auth.security.type\", \"Type\")}{\" \"}\n <code className=\"px-1 py-0.5 rounded bg-bg-sunken text-status-error-text font-mono text-sm\">\n {DELETE_ACCOUNT_CONFIRMATION}\n </code>{\" \"}\n {tr(\"auth.security.toConfirm\", \"to confirm\")}\n </label>\n <input\n id={deleteConfirmId}\n type=\"text\"\n value={deleteConfirmPhrase}\n onChange={(e) => setDeleteConfirmPhrase(e.target.value)}\n aria-required=\"true\"\n aria-label={tr(\n \"auth.security.deleteConfirmAriaLabel\",\n \"Type DELETE MY ACCOUNT to confirm\",\n )}\n aria-describedby={accountError ? deleteErrorId : undefined}\n aria-invalid={\n deleteConfirmPhrase.length > 0 &&\n deleteConfirmPhrase !== DELETE_ACCOUNT_CONFIRMATION\n }\n autoComplete=\"off\"\n className=\"w-full px-4 py-2 rounded-lg border border-border-default bg-bg-surface text-text-primary focus:ring-2 focus:ring-status-error-bg focus:border-transparent font-mono\"\n placeholder={tr(\n \"auth.security.deleteConfirmPlaceholder\",\n \"DELETE MY ACCOUNT\",\n )}\n />\n </div>\n <div className=\"flex gap-3\">\n <button\n type=\"button\"\n onClick={closeModals}\n className=\"flex-1 px-4 py-2 rounded-lg border border-border-default text-text-primary font-medium hover:bg-bg-sunken transition-colors\"\n >\n {tr(\"auth.security.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleDeleteAccount()}\n disabled={\n !accountPassword ||\n deleteConfirmPhrase !== DELETE_ACCOUNT_CONFIRMATION ||\n isDeleting\n }\n className=\"flex-1 px-4 py-2 rounded-lg bg-status-error-bg text-white font-medium hover:bg-status-error-bg-hover disabled:opacity-50 disabled:cursor-not-allowed transition-colors flex items-center justify-center gap-2\"\n >\n {isDeleting && (\n <SpinnerIcon className=\"size-4 animate-spin\" />\n )}\n {tr(\"auth.security.deleteForever\", \"Delete Forever\")}\n </button>\n </div>\n </div>\n </div>\n )}\n\n <NextSteps\n storageKey=\"auth-security\"\n steps={[\n ...(user?.mfaEnabled\n ? []\n : [\n {\n id: \"enable-mfa\",\n label: tr(\n \"auth.security.nextEnableMfa\",\n \"Turn on two-factor authentication\",\n ),\n description: tr(\n \"auth.security.nextEnableMfaDesc\",\n \"The single biggest step you can take to protect your account.\",\n ),\n onClick: () => navigate(\"/auth/mfa\"),\n },\n ]),\n {\n id: \"review-profile\",\n label: tr(\n \"auth.security.nextProfile\",\n \"Review your profile details\",\n ),\n description: tr(\n \"auth.security.nextProfileDesc\",\n \"Make sure your name, email, and contact info are current.\",\n ),\n onClick: () => navigate(\"/auth/profile\"),\n },\n {\n id: \"change-password\",\n label: tr(\"auth.security.nextPassword\", \"Update your password\"),\n description: tr(\n \"auth.security.nextPasswordDesc\",\n \"Set a strong, unique password if you haven't recently.\",\n ),\n onClick: () => navigate(\"/auth/reset-password\"),\n },\n ]}\n />\n\n {/* Footer */}\n <footer className=\"mt-12 pt-6 border-t border-border-subtle \">\n <div\n className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 text-sm text-text-secondary \"\n data-tour=\"auth-footer-actions\"\n >\n <p>\n {tr(\"auth.security.signedInAs\", \"Signed in as\")}{\" \"}\n <span className=\"font-medium text-text-primary \">\n {user?.email}\n </span>\n </p>\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/login\")}\n className=\"hover:text-text-primary transition-colors\"\n >\n {tr(\"auth.security.switchAccount\", \"Switch Account\")}\n </button>\n <span className=\"text-text-muted\" aria-hidden=\"true\">\n |\n </span>\n <button\n type=\"button\"\n onClick={() => setShowLogoutDialog(true)}\n className=\"hover:text-status-error-text transition-colors\"\n >\n {tr(\"auth.security.signOut\", \"Sign Out\")}\n </button>\n </div>\n </div>\n </footer>\n </main>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isAllowedSsoOriginString as e } from "../utils/ssoOrigins.js";
|
|
2
2
|
import { useEffect as t, useRef as n } from "react";
|
|
3
3
|
import { jsx as r } from "react/jsx-runtime";
|
|
4
|
-
|
|
4
|
+
// src/auth/pages/SessionBridgePage.tsx
|
|
5
5
|
var i = "burdenoff-auth-bridge", a = "burdenoff-auth-client", o = "bf-p", s = "bf-profiles", c = "bf-active-profile";
|
|
6
6
|
function l(e) {
|
|
7
7
|
return e !== null && typeof e.postMessage == "function";
|
|
@@ -101,11 +101,7 @@ function b() {
|
|
|
101
101
|
return t(() => {
|
|
102
102
|
async function e() {
|
|
103
103
|
try {
|
|
104
|
-
if (!document.requestStorageAccess) {
|
|
105
|
-
g.current = !0;
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
if (await document.hasStorageAccess()) {
|
|
104
|
+
if (!document.requestStorageAccess || await document.hasStorageAccess()) {
|
|
109
105
|
g.current = !0;
|
|
110
106
|
return;
|
|
111
107
|
}
|
|
@@ -199,15 +195,13 @@ function b() {
|
|
|
199
195
|
}
|
|
200
196
|
}, o));
|
|
201
197
|
break;
|
|
202
|
-
case "AUTH_ACCOUNT_SWITCH":
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}, o));
|
|
210
|
-
break;
|
|
198
|
+
case "AUTH_ACCOUNT_SWITCH": r.payload && (f(r.payload), n({
|
|
199
|
+
type: "AUTH_STATE_CHANGED",
|
|
200
|
+
payload: {
|
|
201
|
+
...d(),
|
|
202
|
+
trigger: "account_switch"
|
|
203
|
+
}
|
|
204
|
+
}, o));
|
|
211
205
|
}
|
|
212
206
|
}
|
|
213
207
|
function g(e) {
|
|
@@ -228,7 +222,6 @@ function b() {
|
|
|
228
222
|
style: { display: "none" }
|
|
229
223
|
});
|
|
230
224
|
}
|
|
231
|
-
//#endregion
|
|
232
225
|
export { b as SessionBridgePage };
|
|
233
226
|
|
|
234
227
|
//# sourceMappingURL=SessionBridgePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionBridgePage.js","names":[],"sources":["../../../src/auth/pages/SessionBridgePage.tsx"],"sourcesContent":["/**\n * SessionBridgePage — Cross-Domain Auth Session Bridge (Profile-Scoped)\n *\n * Embedded as hidden iframe by other app shells. Manages profile-scoped\n * auth tokens in localStorage and relays state via postMessage.\n *\n * Storage uses ProfileStorageManager keys:\n * bf-profiles, bf-p-{id}-user, bf-p-{id}-tokens, bf-p-{id}-workspace-token\n * bf-active-profile (sessionStorage, per-tab)\n *\n * URL: /auth/session-bridge\n */\n\nimport { useEffect, useRef } from \"react\";\nimport { isAllowedSsoOriginString } from \"../utils/ssoOrigins\";\n\nconst BRIDGE_SOURCE = \"burdenoff-auth-bridge\";\nconst PARENT_SOURCE = \"burdenoff-auth-client\";\nconst BF_PREFIX = \"bf-p\";\nconst BF_PROFILES = \"bf-profiles\";\nconst BF_ACTIVE = \"bf-active-profile\";\n\ninterface BridgeMessage {\n type: string;\n payload?: Record<string, unknown>;\n nonce?: string;\n source?: string;\n}\n\ninterface ConnectedParent {\n source: Window;\n origin: string;\n nonce: string;\n}\n\nfunction isWindowMessageSource(\n source: MessageEventSource | null,\n): source is Window {\n return (\n source !== null &&\n typeof (source as { postMessage?: unknown }).postMessage === \"function\"\n );\n}\n\nfunction isBridgeMessage(value: unknown): value is BridgeMessage {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { type?: unknown }).type === \"string\"\n );\n}\n\nfunction readAuthState(): Record<string, unknown> {\n try {\n const profiles = localStorage.getItem(BF_PROFILES);\n let activeId = sessionStorage.getItem(BF_ACTIVE);\n\n // Fall back: if sessionStorage is empty but localStorage has profiles,\n // pick the first profile (same logic as resolveActiveProfileId)\n if (!activeId && profiles) {\n try {\n const profileList = JSON.parse(profiles) as Array<{ id: string }>;\n if (profileList.length > 0) {\n activeId = profileList[0].id;\n sessionStorage.setItem(BF_ACTIVE, activeId);\n }\n } catch {\n // ignore\n }\n }\n\n if (!activeId) {\n return {\n user: null,\n tokens: null,\n profiles: profiles ? JSON.parse(profiles) : [],\n activeProfileId: null,\n };\n }\n const user = localStorage.getItem(`${BF_PREFIX}-${activeId}-user`);\n const tokens = localStorage.getItem(`${BF_PREFIX}-${activeId}-tokens`);\n const wsToken = localStorage.getItem(\n `${BF_PREFIX}-${activeId}-workspace-token`,\n );\n\n return {\n user: user ? JSON.parse(user) : null,\n tokens: tokens\n ? { ...JSON.parse(tokens), workspaceToken: wsToken ?? undefined }\n : null,\n profiles: profiles ? JSON.parse(profiles) : [],\n activeProfileId: activeId,\n };\n } catch {\n return { user: null, tokens: null, profiles: [], activeProfileId: null };\n }\n}\n\nfunction writeAuthState(payload: Record<string, unknown>): void {\n const profileId =\n (payload.activeProfileId as string) ??\n ((payload.user as Record<string, unknown>)?.id as string) ??\n sessionStorage.getItem(BF_ACTIVE);\n if (!profileId) return;\n\n if (payload.user) {\n localStorage.setItem(\n `${BF_PREFIX}-${profileId}-user`,\n JSON.stringify(payload.user),\n );\n }\n if (payload.tokens && typeof payload.tokens === \"object\") {\n const t = payload.tokens as Record<string, unknown>;\n const { workspaceToken, ...rest } = t;\n localStorage.setItem(\n `${BF_PREFIX}-${profileId}-tokens`,\n JSON.stringify(rest),\n );\n if (workspaceToken) {\n localStorage.setItem(\n `${BF_PREFIX}-${profileId}-workspace-token`,\n String(workspaceToken),\n );\n }\n }\n if (payload.profiles) {\n localStorage.setItem(BF_PROFILES, JSON.stringify(payload.profiles));\n }\n sessionStorage.setItem(BF_ACTIVE, profileId);\n}\n\nfunction clearAuthState(): void {\n const activeId = sessionStorage.getItem(BF_ACTIVE);\n if (activeId) {\n localStorage.removeItem(`${BF_PREFIX}-${activeId}-user`);\n localStorage.removeItem(`${BF_PREFIX}-${activeId}-tokens`);\n localStorage.removeItem(`${BF_PREFIX}-${activeId}-workspace-token`);\n localStorage.removeItem(`${BF_PREFIX}-${activeId}-auth-storage`);\n // Remove from profiles list\n try {\n const raw = localStorage.getItem(BF_PROFILES);\n const profiles = raw ? JSON.parse(raw) : [];\n localStorage.setItem(\n BF_PROFILES,\n JSON.stringify(\n profiles.filter((p: { id: string }) => p.id !== activeId),\n ),\n );\n } catch {\n /* ignore */\n }\n sessionStorage.removeItem(BF_ACTIVE);\n }\n localStorage.removeItem(\"auth-storage\");\n}\n\nfunction clearAllAuthState(): void {\n // Get all profiles and remove their data\n try {\n const raw = localStorage.getItem(BF_PROFILES);\n const profiles: Array<{ id: string }> = raw ? JSON.parse(raw) : [];\n for (const p of profiles) {\n localStorage.removeItem(`${BF_PREFIX}-${p.id}-user`);\n localStorage.removeItem(`${BF_PREFIX}-${p.id}-tokens`);\n localStorage.removeItem(`${BF_PREFIX}-${p.id}-workspace-token`);\n localStorage.removeItem(`${BF_PREFIX}-${p.id}-auth-storage`);\n localStorage.removeItem(`${BF_PREFIX}-${p.id}-context`);\n }\n } catch {\n /* ignore */\n }\n for (const key of Object.keys(localStorage)) {\n if (key.startsWith(`${BF_PREFIX}-`) && key.endsWith(\"-auth-storage\")) {\n localStorage.removeItem(key);\n }\n }\n localStorage.removeItem(BF_PROFILES);\n localStorage.removeItem(\"auth-storage\");\n sessionStorage.removeItem(BF_ACTIVE);\n}\n\n// SSO cookie helpers (shared domain cookie for local dev fast-path)\nconst SSO_COOKIE_NAME = \"bf-sso-profile\";\nconst SHELL_SSO_COOKIE_NAME = \"bf-sso\";\nfunction setSSoCookieBridge(profileId: string, origin: string): void {\n try {\n const hostname = window.location.hostname;\n const domain = hostname.endsWith(\".local.burdenoff.com\")\n ? \".local.burdenoff.com\"\n : undefined;\n const domainPart = domain ? `; domain=${domain}` : \"\";\n const securePart = window.location.protocol === \"https:\" ? \"; secure\" : \"\";\n const cookieValue = JSON.stringify({ id: profileId, origin });\n document.cookie = `${SSO_COOKIE_NAME}=${encodeURIComponent(cookieValue)}${domainPart}; path=/; samesite=lax${securePart}; max-age=86400`;\n } catch {\n /* ignore */\n }\n}\n\nfunction setShellSsoCookieBridge(profileId: string, origin: string): void {\n try {\n const hostname = window.location.hostname;\n const domain = hostname.endsWith(\".local.burdenoff.com\")\n ? \".local.burdenoff.com\"\n : undefined;\n const domainPart = domain ? `; domain=${domain}` : \"\";\n const securePart = window.location.protocol === \"https:\" ? \"; secure\" : \"\";\n const ssoValue = JSON.stringify({ id: profileId, origin });\n document.cookie = `${SHELL_SSO_COOKIE_NAME}=${encodeURIComponent(ssoValue)}${domainPart}; path=/; samesite=lax${securePart}; max-age=86400`;\n } catch {\n /* ignore */\n }\n}\n\nfunction clearSSoCookieBridge(): void {\n try {\n const hostname = window.location.hostname;\n const domain = hostname.endsWith(\".local.burdenoff.com\")\n ? \".local.burdenoff.com\"\n : undefined;\n document.cookie = `${SSO_COOKIE_NAME}=; path=/; max-age=0`;\n document.cookie = `${SHELL_SSO_COOKIE_NAME}=; path=/; max-age=0`;\n if (domain) {\n document.cookie = `${SSO_COOKIE_NAME}=; path=/; domain=${domain}; max-age=0`;\n document.cookie = `${SHELL_SSO_COOKIE_NAME}=; path=/; domain=${domain}; max-age=0`;\n }\n if (hostname && ![\"localhost\", \"127.0.0.1\"].includes(hostname)) {\n document.cookie = `${SSO_COOKIE_NAME}=; path=/; domain=${hostname}; max-age=0`;\n document.cookie = `${SHELL_SSO_COOKIE_NAME}=; path=/; domain=${hostname}; max-age=0`;\n }\n } catch {\n /* ignore */\n }\n}\n\nexport function SessionBridgePage() {\n const connectedParents = useRef<Map<Window, ConnectedParent>>(new Map());\n const readySentTo = useRef<Map<Window, string>>(new Map());\n const storageAccessGranted = useRef(false);\n\n // Request unpartitioned storage access (Storage Access API).\n // This is critical for cross-domain SSO in modern browsers that\n // partition third-party iframe localStorage.\n useEffect(() => {\n async function requestAccess() {\n try {\n // Check if Storage Access API is available\n if (!document.requestStorageAccess) {\n storageAccessGranted.current = true;\n return;\n }\n // Check if we already have access\n const hasAccess = await document.hasStorageAccess();\n if (hasAccess) {\n storageAccessGranted.current = true;\n return;\n }\n // Request unpartitioned storage access\n await document.requestStorageAccess();\n storageAccessGranted.current = true;\n } catch {\n // Storage Access API not available or denied — fall through\n // to cookie-based fallback. localStorage will be partitioned.\n storageAccessGranted.current = false;\n }\n }\n void requestAccess();\n }, []);\n\n useEffect(() => {\n function isOriginAllowed(origin: string): boolean {\n return isAllowedSsoOriginString(origin);\n }\n\n function broadcastToAll(\n message: BridgeMessage,\n excludeSource?: Window | null,\n ): void {\n connectedParents.current.forEach((connection, parent) => {\n if (parent !== excludeSource) {\n connection.source.postMessage(\n { ...message, source: BRIDGE_SOURCE, nonce: connection.nonce },\n connection.origin,\n );\n }\n });\n }\n\n function handleMessage(event: MessageEvent): void {\n if (!isOriginAllowed(event.origin)) return;\n if (!isBridgeMessage(event.data) || event.data.source !== PARENT_SOURCE) {\n return;\n }\n const message = event.data;\n const parentSource = isWindowMessageSource(event.source)\n ? event.source\n : null;\n if (!parentSource) return;\n\n const incomingNonce =\n typeof message.nonce === \"string\" && message.nonce.length > 0\n ? message.nonce\n : null;\n const existingConnection = connectedParents.current.get(parentSource);\n\n if (message.type !== \"AUTH_INIT\") {\n if (!incomingNonce || existingConnection?.nonce !== incomingNonce) {\n return;\n }\n } else if (!incomingNonce) {\n return;\n }\n\n switch (message.type) {\n case \"AUTH_INIT\": {\n connectedParents.current.set(parentSource, {\n source: parentSource,\n origin: event.origin,\n nonce: incomingNonce,\n });\n\n // If the parent sends its current auth state in AUTH_INIT,\n // store it so other cross-domain shells can pick it up.\n if (message.payload && typeof message.payload === \"object\") {\n const initPayload = message.payload as Record<string, unknown>;\n if (initPayload.user && initPayload.tokens) {\n writeAuthState(initPayload);\n }\n }\n\n const state = readAuthState();\n const connection = connectedParents.current.get(parentSource);\n if (connection) {\n connection.source.postMessage(\n {\n type: \"AUTH_STATE\",\n payload: state,\n source: BRIDGE_SOURCE,\n nonce: connection.nonce,\n },\n event.origin,\n );\n if (readySentTo.current.get(parentSource) !== connection.nonce) {\n readySentTo.current.set(parentSource, connection.nonce);\n connection.source.postMessage(\n {\n type: \"AUTH_BRIDGE_READY\",\n source: BRIDGE_SOURCE,\n nonce: connection.nonce,\n },\n event.origin,\n );\n }\n }\n break;\n }\n case \"AUTH_LOGIN\": {\n if (message.payload) {\n writeAuthState(message.payload);\n // Set SSO cookie for cross-domain fast-path detection\n const loginPayload = message.payload as Record<string, unknown>;\n const loginProfileId =\n (loginPayload.activeProfileId as string) ??\n ((loginPayload.user as Record<string, unknown>)?.id as string);\n if (loginProfileId) {\n clearSSoCookieBridge();\n setSSoCookieBridge(loginProfileId, event.origin);\n setShellSsoCookieBridge(loginProfileId, event.origin);\n }\n broadcastToAll(\n {\n type: \"AUTH_STATE_CHANGED\",\n payload: { ...readAuthState(), trigger: \"login\" },\n },\n parentSource,\n );\n }\n break;\n }\n case \"AUTH_LOGOUT\": {\n clearAuthState();\n clearSSoCookieBridge();\n broadcastToAll(\n {\n type: \"AUTH_STATE_CHANGED\",\n payload: { ...readAuthState(), trigger: \"logout\" },\n },\n parentSource,\n );\n break;\n }\n case \"AUTH_LOGOUT_ALL\": {\n clearAllAuthState();\n clearSSoCookieBridge();\n broadcastToAll(\n {\n type: \"AUTH_STATE_CHANGED\",\n payload: { ...readAuthState(), trigger: \"logout_all\" },\n },\n parentSource,\n );\n break;\n }\n case \"AUTH_TOKEN_REFRESH\": {\n if (message.payload?.tokens) {\n writeAuthState({ tokens: message.payload.tokens });\n broadcastToAll(\n {\n type: \"AUTH_STATE_CHANGED\",\n payload: { ...readAuthState(), trigger: \"refresh\" },\n },\n parentSource,\n );\n }\n break;\n }\n case \"AUTH_ACCOUNT_SWITCH\": {\n if (message.payload) {\n writeAuthState(message.payload);\n broadcastToAll(\n {\n type: \"AUTH_STATE_CHANGED\",\n payload: { ...readAuthState(), trigger: \"account_switch\" },\n },\n parentSource,\n );\n }\n break;\n }\n }\n }\n\n function handleStorageChange(event: StorageEvent): void {\n if (event.key?.startsWith(BF_PREFIX) || event.key === BF_PROFILES) {\n broadcastToAll({\n type: \"AUTH_STATE_CHANGED\",\n payload: { ...readAuthState(), trigger: \"storage_sync\" },\n });\n }\n }\n\n window.addEventListener(\"message\", handleMessage);\n window.addEventListener(\"storage\", handleStorageChange);\n\n return () => {\n window.removeEventListener(\"message\", handleMessage);\n window.removeEventListener(\"storage\", handleStorageChange);\n // Don't clear connectedParents or readySentTo on cleanup to maintain state across StrictMode remounts\n };\n }, []);\n\n return (\n <div\n id=\"auth-session-bridge-root\"\n data-bridge=\"active\"\n style={{ display: \"none\" }}\n />\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SessionBridgePage.js","names":["BRIDGE_SOURCE","PARENT_SOURCE","BF_PREFIX","BF_PROFILES","BF_ACTIVE","isWindowMessageSource","source","isBridgeMessage","value","readAuthState","profiles","activeId","profileList","user","tokens","wsToken","writeAuthState","profileId","payload","rest","workspaceToken","clearAuthState","raw","p","clearAllAuthState","key","SSO_COOKIE_NAME","SHELL_SSO_COOKIE_NAME","setSSoCookieBridge","domain","domainPart","securePart","cookieValue","setShellSsoCookieBridge","ssoValue","clearSSoCookieBridge","hostname","SessionBridgePage","connectedParents","useRef","readySentTo","storageAccessGranted","useEffect","requestAccess","isOriginAllowed","isAllowedSsoOriginString","origin","broadcastToAll","parent","excludeSource","connection","message","handleMessage","event","parentSource","incomingNonce","existingConnection","initPayload","state","loginPayload","loginProfileId","handleStorageChange"],"sources":["../../../src/auth/pages/SessionBridgePage.tsx"],"sourcesContent":["/**\n * SessionBridgePage — Cross-Domain Auth Session Bridge (Profile-Scoped)\n *\n * Embedded as hidden iframe by other app shells. Manages profile-scoped\n * auth tokens in localStorage and relays state via postMessage.\n *\n * Storage uses ProfileStorageManager keys:\n * bf-profiles, bf-p-{id}-user, bf-p-{id}-tokens, bf-p-{id}-workspace-token\n * bf-active-profile (sessionStorage, per-tab)\n *\n * URL: /auth/session-bridge\n */\n\nimport { useEffect, useRef } from \"react\";\nimport { isAllowedSsoOriginString } from \"../utils/ssoOrigins\";\n\nconst BRIDGE_SOURCE = \"burdenoff-auth-bridge\";\nconst PARENT_SOURCE = \"burdenoff-auth-client\";\nconst BF_PREFIX = \"bf-p\";\nconst BF_PROFILES = \"bf-profiles\";\nconst BF_ACTIVE = \"bf-active-profile\";\n\ninterface BridgeMessage {\n type: string;\n payload?: Record<string, unknown>;\n nonce?: string;\n source?: string;\n}\n\ninterface ConnectedParent {\n source: Window;\n origin: string;\n nonce: string;\n}\n\nfunction isWindowMessageSource(\n source: MessageEventSource | null,\n): source is Window {\n return (\n source !== null &&\n typeof (source as { postMessage?: unknown }).postMessage === \"function\"\n );\n}\n\nfunction isBridgeMessage(value: unknown): value is BridgeMessage {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { type?: unknown }).type === \"string\"\n );\n}\n\nfunction readAuthState(): Record<string, unknown> {\n try {\n const profiles = localStorage.getItem(BF_PROFILES);\n let activeId = sessionStorage.getItem(BF_ACTIVE);\n\n // Fall back: if sessionStorage is empty but localStorage has profiles,\n // pick the first profile (same logic as resolveActiveProfileId)\n if (!activeId && profiles) {\n try {\n const profileList = JSON.parse(profiles) as Array<{ id: string }>;\n if (profileList.length > 0) {\n activeId = profileList[0].id;\n sessionStorage.setItem(BF_ACTIVE, activeId);\n }\n } catch {\n // ignore\n }\n }\n\n if (!activeId) {\n return {\n user: null,\n tokens: null,\n profiles: profiles ? JSON.parse(profiles) : [],\n activeProfileId: null,\n };\n }\n const user = localStorage.getItem(`${BF_PREFIX}-${activeId}-user`);\n const tokens = localStorage.getItem(`${BF_PREFIX}-${activeId}-tokens`);\n const wsToken = localStorage.getItem(\n `${BF_PREFIX}-${activeId}-workspace-token`,\n );\n\n return {\n user: user ? JSON.parse(user) : null,\n tokens: tokens\n ? { ...JSON.parse(tokens), workspaceToken: wsToken ?? undefined }\n : null,\n profiles: profiles ? JSON.parse(profiles) : [],\n activeProfileId: activeId,\n };\n } catch {\n return { user: null, tokens: null, profiles: [], activeProfileId: null };\n }\n}\n\nfunction writeAuthState(payload: Record<string, unknown>): void {\n const profileId =\n (payload.activeProfileId as string) ??\n ((payload.user as Record<string, unknown>)?.id as string) ??\n sessionStorage.getItem(BF_ACTIVE);\n if (!profileId) return;\n\n if (payload.user) {\n localStorage.setItem(\n `${BF_PREFIX}-${profileId}-user`,\n JSON.stringify(payload.user),\n );\n }\n if (payload.tokens && typeof payload.tokens === \"object\") {\n const t = payload.tokens as Record<string, unknown>;\n const { workspaceToken, ...rest } = t;\n localStorage.setItem(\n `${BF_PREFIX}-${profileId}-tokens`,\n JSON.stringify(rest),\n );\n if (workspaceToken) {\n localStorage.setItem(\n `${BF_PREFIX}-${profileId}-workspace-token`,\n String(workspaceToken),\n );\n }\n }\n if (payload.profiles) {\n localStorage.setItem(BF_PROFILES, JSON.stringify(payload.profiles));\n }\n sessionStorage.setItem(BF_ACTIVE, profileId);\n}\n\nfunction clearAuthState(): void {\n const activeId = sessionStorage.getItem(BF_ACTIVE);\n if (activeId) {\n localStorage.removeItem(`${BF_PREFIX}-${activeId}-user`);\n localStorage.removeItem(`${BF_PREFIX}-${activeId}-tokens`);\n localStorage.removeItem(`${BF_PREFIX}-${activeId}-workspace-token`);\n localStorage.removeItem(`${BF_PREFIX}-${activeId}-auth-storage`);\n // Remove from profiles list\n try {\n const raw = localStorage.getItem(BF_PROFILES);\n const profiles = raw ? JSON.parse(raw) : [];\n localStorage.setItem(\n BF_PROFILES,\n JSON.stringify(\n profiles.filter((p: { id: string }) => p.id !== activeId),\n ),\n );\n } catch {\n /* ignore */\n }\n sessionStorage.removeItem(BF_ACTIVE);\n }\n localStorage.removeItem(\"auth-storage\");\n}\n\nfunction clearAllAuthState(): void {\n // Get all profiles and remove their data\n try {\n const raw = localStorage.getItem(BF_PROFILES);\n const profiles: Array<{ id: string }> = raw ? JSON.parse(raw) : [];\n for (const p of profiles) {\n localStorage.removeItem(`${BF_PREFIX}-${p.id}-user`);\n localStorage.removeItem(`${BF_PREFIX}-${p.id}-tokens`);\n localStorage.removeItem(`${BF_PREFIX}-${p.id}-workspace-token`);\n localStorage.removeItem(`${BF_PREFIX}-${p.id}-auth-storage`);\n localStorage.removeItem(`${BF_PREFIX}-${p.id}-context`);\n }\n } catch {\n /* ignore */\n }\n for (const key of Object.keys(localStorage)) {\n if (key.startsWith(`${BF_PREFIX}-`) && key.endsWith(\"-auth-storage\")) {\n localStorage.removeItem(key);\n }\n }\n localStorage.removeItem(BF_PROFILES);\n localStorage.removeItem(\"auth-storage\");\n sessionStorage.removeItem(BF_ACTIVE);\n}\n\n// SSO cookie helpers (shared domain cookie for local dev fast-path)\nconst SSO_COOKIE_NAME = \"bf-sso-profile\";\nconst SHELL_SSO_COOKIE_NAME = \"bf-sso\";\nfunction setSSoCookieBridge(profileId: string, origin: string): void {\n try {\n const hostname = window.location.hostname;\n const domain = hostname.endsWith(\".local.burdenoff.com\")\n ? \".local.burdenoff.com\"\n : undefined;\n const domainPart = domain ? `; domain=${domain}` : \"\";\n const securePart = window.location.protocol === \"https:\" ? \"; secure\" : \"\";\n const cookieValue = JSON.stringify({ id: profileId, origin });\n document.cookie = `${SSO_COOKIE_NAME}=${encodeURIComponent(cookieValue)}${domainPart}; path=/; samesite=lax${securePart}; max-age=86400`;\n } catch {\n /* ignore */\n }\n}\n\nfunction setShellSsoCookieBridge(profileId: string, origin: string): void {\n try {\n const hostname = window.location.hostname;\n const domain = hostname.endsWith(\".local.burdenoff.com\")\n ? \".local.burdenoff.com\"\n : undefined;\n const domainPart = domain ? `; domain=${domain}` : \"\";\n const securePart = window.location.protocol === \"https:\" ? \"; secure\" : \"\";\n const ssoValue = JSON.stringify({ id: profileId, origin });\n document.cookie = `${SHELL_SSO_COOKIE_NAME}=${encodeURIComponent(ssoValue)}${domainPart}; path=/; samesite=lax${securePart}; max-age=86400`;\n } catch {\n /* ignore */\n }\n}\n\nfunction clearSSoCookieBridge(): void {\n try {\n const hostname = window.location.hostname;\n const domain = hostname.endsWith(\".local.burdenoff.com\")\n ? \".local.burdenoff.com\"\n : undefined;\n document.cookie = `${SSO_COOKIE_NAME}=; path=/; max-age=0`;\n document.cookie = `${SHELL_SSO_COOKIE_NAME}=; path=/; max-age=0`;\n if (domain) {\n document.cookie = `${SSO_COOKIE_NAME}=; path=/; domain=${domain}; max-age=0`;\n document.cookie = `${SHELL_SSO_COOKIE_NAME}=; path=/; domain=${domain}; max-age=0`;\n }\n if (hostname && ![\"localhost\", \"127.0.0.1\"].includes(hostname)) {\n document.cookie = `${SSO_COOKIE_NAME}=; path=/; domain=${hostname}; max-age=0`;\n document.cookie = `${SHELL_SSO_COOKIE_NAME}=; path=/; domain=${hostname}; max-age=0`;\n }\n } catch {\n /* ignore */\n }\n}\n\nexport function SessionBridgePage() {\n const connectedParents = useRef<Map<Window, ConnectedParent>>(new Map());\n const readySentTo = useRef<Map<Window, string>>(new Map());\n const storageAccessGranted = useRef(false);\n\n // Request unpartitioned storage access (Storage Access API).\n // This is critical for cross-domain SSO in modern browsers that\n // partition third-party iframe localStorage.\n useEffect(() => {\n async function requestAccess() {\n try {\n // Check if Storage Access API is available\n if (!document.requestStorageAccess) {\n storageAccessGranted.current = true;\n return;\n }\n // Check if we already have access\n const hasAccess = await document.hasStorageAccess();\n if (hasAccess) {\n storageAccessGranted.current = true;\n return;\n }\n // Request unpartitioned storage access\n await document.requestStorageAccess();\n storageAccessGranted.current = true;\n } catch {\n // Storage Access API not available or denied — fall through\n // to cookie-based fallback. localStorage will be partitioned.\n storageAccessGranted.current = false;\n }\n }\n void requestAccess();\n }, []);\n\n useEffect(() => {\n function isOriginAllowed(origin: string): boolean {\n return isAllowedSsoOriginString(origin);\n }\n\n function broadcastToAll(\n message: BridgeMessage,\n excludeSource?: Window | null,\n ): void {\n connectedParents.current.forEach((connection, parent) => {\n if (parent !== excludeSource) {\n connection.source.postMessage(\n { ...message, source: BRIDGE_SOURCE, nonce: connection.nonce },\n connection.origin,\n );\n }\n });\n }\n\n function handleMessage(event: MessageEvent): void {\n if (!isOriginAllowed(event.origin)) return;\n if (!isBridgeMessage(event.data) || event.data.source !== PARENT_SOURCE) {\n return;\n }\n const message = event.data;\n const parentSource = isWindowMessageSource(event.source)\n ? event.source\n : null;\n if (!parentSource) return;\n\n const incomingNonce =\n typeof message.nonce === \"string\" && message.nonce.length > 0\n ? message.nonce\n : null;\n const existingConnection = connectedParents.current.get(parentSource);\n\n if (message.type !== \"AUTH_INIT\") {\n if (!incomingNonce || existingConnection?.nonce !== incomingNonce) {\n return;\n }\n } else if (!incomingNonce) {\n return;\n }\n\n switch (message.type) {\n case \"AUTH_INIT\": {\n connectedParents.current.set(parentSource, {\n source: parentSource,\n origin: event.origin,\n nonce: incomingNonce,\n });\n\n // If the parent sends its current auth state in AUTH_INIT,\n // store it so other cross-domain shells can pick it up.\n if (message.payload && typeof message.payload === \"object\") {\n const initPayload = message.payload as Record<string, unknown>;\n if (initPayload.user && initPayload.tokens) {\n writeAuthState(initPayload);\n }\n }\n\n const state = readAuthState();\n const connection = connectedParents.current.get(parentSource);\n if (connection) {\n connection.source.postMessage(\n {\n type: \"AUTH_STATE\",\n payload: state,\n source: BRIDGE_SOURCE,\n nonce: connection.nonce,\n },\n event.origin,\n );\n if (readySentTo.current.get(parentSource) !== connection.nonce) {\n readySentTo.current.set(parentSource, connection.nonce);\n connection.source.postMessage(\n {\n type: \"AUTH_BRIDGE_READY\",\n source: BRIDGE_SOURCE,\n nonce: connection.nonce,\n },\n event.origin,\n );\n }\n }\n break;\n }\n case \"AUTH_LOGIN\": {\n if (message.payload) {\n writeAuthState(message.payload);\n // Set SSO cookie for cross-domain fast-path detection\n const loginPayload = message.payload as Record<string, unknown>;\n const loginProfileId =\n (loginPayload.activeProfileId as string) ??\n ((loginPayload.user as Record<string, unknown>)?.id as string);\n if (loginProfileId) {\n clearSSoCookieBridge();\n setSSoCookieBridge(loginProfileId, event.origin);\n setShellSsoCookieBridge(loginProfileId, event.origin);\n }\n broadcastToAll(\n {\n type: \"AUTH_STATE_CHANGED\",\n payload: { ...readAuthState(), trigger: \"login\" },\n },\n parentSource,\n );\n }\n break;\n }\n case \"AUTH_LOGOUT\": {\n clearAuthState();\n clearSSoCookieBridge();\n broadcastToAll(\n {\n type: \"AUTH_STATE_CHANGED\",\n payload: { ...readAuthState(), trigger: \"logout\" },\n },\n parentSource,\n );\n break;\n }\n case \"AUTH_LOGOUT_ALL\": {\n clearAllAuthState();\n clearSSoCookieBridge();\n broadcastToAll(\n {\n type: \"AUTH_STATE_CHANGED\",\n payload: { ...readAuthState(), trigger: \"logout_all\" },\n },\n parentSource,\n );\n break;\n }\n case \"AUTH_TOKEN_REFRESH\": {\n if (message.payload?.tokens) {\n writeAuthState({ tokens: message.payload.tokens });\n broadcastToAll(\n {\n type: \"AUTH_STATE_CHANGED\",\n payload: { ...readAuthState(), trigger: \"refresh\" },\n },\n parentSource,\n );\n }\n break;\n }\n case \"AUTH_ACCOUNT_SWITCH\": {\n if (message.payload) {\n writeAuthState(message.payload);\n broadcastToAll(\n {\n type: \"AUTH_STATE_CHANGED\",\n payload: { ...readAuthState(), trigger: \"account_switch\" },\n },\n parentSource,\n );\n }\n break;\n }\n }\n }\n\n function handleStorageChange(event: StorageEvent): void {\n if (event.key?.startsWith(BF_PREFIX) || event.key === BF_PROFILES) {\n broadcastToAll({\n type: \"AUTH_STATE_CHANGED\",\n payload: { ...readAuthState(), trigger: \"storage_sync\" },\n });\n }\n }\n\n window.addEventListener(\"message\", handleMessage);\n window.addEventListener(\"storage\", handleStorageChange);\n\n return () => {\n window.removeEventListener(\"message\", handleMessage);\n window.removeEventListener(\"storage\", handleStorageChange);\n // Don't clear connectedParents or readySentTo on cleanup to maintain state across StrictMode remounts\n };\n }, []);\n\n return (\n <div\n id=\"auth-session-bridge-root\"\n data-bridge=\"active\"\n style={{ display: \"none\" }}\n />\n );\n}\n"],"mappings":"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"}
|