@burdenoff/microfe-auth 2026.830.1 → 2026.904.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/auth/AuthRoot.js +43 -41
  2. package/dist/auth/AuthRoot.js.map +1 -1
  3. package/dist/auth/AuthRoutes.js +7 -0
  4. package/dist/auth/AuthRoutes.js.map +1 -1
  5. package/dist/auth/components/AuthLayout.js +65 -67
  6. package/dist/auth/components/AuthLayout.js.map +1 -1
  7. package/dist/auth/components/CaptchaV2.js +21 -24
  8. package/dist/auth/components/CaptchaV2.js.map +1 -1
  9. package/dist/auth/components/ConfirmActionDialog.js +33 -32
  10. package/dist/auth/components/ConfirmActionDialog.js.map +1 -1
  11. package/dist/auth/components/profile/UserProfileMenu.js +44 -46
  12. package/dist/auth/components/profile/UserProfileMenu.js.map +1 -1
  13. package/dist/auth/components/workspace/WorkspaceSelector.js +62 -68
  14. package/dist/auth/components/workspace/WorkspaceSelector.js.map +1 -1
  15. package/dist/auth/pages/AuditLogPage.js +65 -68
  16. package/dist/auth/pages/AuditLogPage.js.map +1 -1
  17. package/dist/auth/pages/EmailVerifyPage.js +51 -54
  18. package/dist/auth/pages/EmailVerifyPage.js.map +1 -1
  19. package/dist/auth/pages/LoginPage.js +223 -225
  20. package/dist/auth/pages/LoginPage.js.map +1 -1
  21. package/dist/auth/pages/MagicLinkPage.js +67 -69
  22. package/dist/auth/pages/MagicLinkPage.js.map +1 -1
  23. package/dist/auth/pages/MfaSetupPage.js +85 -88
  24. package/dist/auth/pages/MfaSetupPage.js.map +1 -1
  25. package/dist/auth/pages/MfaVerifyPage.js +60 -63
  26. package/dist/auth/pages/MfaVerifyPage.js.map +1 -1
  27. package/dist/auth/pages/OAuthCallbackPage.js +37 -40
  28. package/dist/auth/pages/OAuthCallbackPage.js.map +1 -1
  29. package/dist/auth/pages/PasswordResetPage.js +102 -105
  30. package/dist/auth/pages/PasswordResetPage.js.map +1 -1
  31. package/dist/auth/pages/ReactivatePage.js +54 -57
  32. package/dist/auth/pages/ReactivatePage.js.map +1 -1
  33. package/dist/auth/pages/RegisterPage.js +171 -174
  34. package/dist/auth/pages/RegisterPage.js.map +1 -1
  35. package/dist/auth/pages/SSOClearPage.js +21 -20
  36. package/dist/auth/pages/SSOClearPage.js.map +1 -1
  37. package/dist/auth/pages/SSODepositPage.js +27 -26
  38. package/dist/auth/pages/SSODepositPage.js.map +1 -1
  39. package/dist/auth/pages/SSORelayCallbackPage.js +19 -18
  40. package/dist/auth/pages/SSORelayCallbackPage.js.map +1 -1
  41. package/dist/auth/pages/SecurityPage.js +247 -250
  42. package/dist/auth/pages/SecurityPage.js.map +1 -1
  43. package/dist/auth/pages/SessionsPage.js +95 -98
  44. package/dist/auth/pages/SessionsPage.js.map +1 -1
  45. package/dist/auth/pages/SsoCallbackPage.js +53 -56
  46. package/dist/auth/pages/SsoCallbackPage.js.map +1 -1
  47. package/dist/auth/pages/SsoConfigPage.js +296 -299
  48. package/dist/auth/pages/SsoConfigPage.js.map +1 -1
  49. package/dist/auth/pages/SsoLoginPage.js +65 -68
  50. package/dist/auth/pages/SsoLoginPage.js.map +1 -1
  51. package/dist/auth/pages/UserDetailsPage.js +124 -127
  52. package/dist/auth/pages/UserDetailsPage.js.map +1 -1
  53. package/dist/auth/pages/UserProfilePage.js +171 -174
  54. package/dist/auth/pages/UserProfilePage.js.map +1 -1
  55. package/dist/auth/pages/UsersPage.js +322 -325
  56. package/dist/auth/pages/UsersPage.js.map +1 -1
  57. package/dist/index.d.ts +1 -0
  58. package/dist/index.js +2 -1
  59. package/dist/shared/hooks/index.d.ts +1 -6
  60. package/dist/shared/hooks/useTr.d.ts +16 -0
  61. package/dist/shared/hooks/useTr.js +14 -0
  62. package/dist/shared/hooks/useTr.js.map +1 -0
  63. package/dist/translations/en.d.ts +806 -248
  64. package/dist/translations/en.js +870 -0
  65. package/dist/translations/en.js.map +1 -0
  66. package/package.json +1 -1
@@ -1,80 +1,77 @@
1
1
  import { useAuthStore as e } from "../store/authStore.js";
2
- import { AlertIcon as t, SpinnerIcon as n } from "../components/AuthIcons.js";
3
- import { useSsoOperations as r } from "../hooks/useSsoOperations.js";
4
- import { useEffect as i, useRef as a, useState as o } from "react";
5
- import { useNavigate as s, useSearchParams as c } from "react-router";
6
- import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell";
2
+ import { useTr as t } from "../../shared/hooks/useTr.js";
3
+ import { AlertIcon as n, SpinnerIcon as r } from "../components/AuthIcons.js";
4
+ import { useSsoOperations as i } from "../hooks/useSsoOperations.js";
5
+ import { useEffect as a, useRef as o, useState as s } from "react";
6
+ import { useNavigate as c, useSearchParams as l } from "react-router";
7
7
  import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
8
8
  import { Button as p } from "@burdenoff/fe-libs/button";
9
9
  //#region src/auth/pages/SsoCallbackPage.tsx
10
10
  function m() {
11
- let { t: m } = l(), h = (e, t) => {
12
- let n = m(e);
13
- return n === e ? t : n;
14
- }, g = s(), [_] = c(), v = e((e) => e.setAuthTokens), y = e((e) => e.setAuthenticated), { exchangeOidcCode: b, validateOidcState: x, isExternalOidcCallback: S, handleExternalOidcCallback: C } = r(), [w, T] = o(null), [E, D] = o("processing"), [O, k] = o("unknown"), A = a(!1), j = a(null);
15
- return i(() => {
16
- if (A.current) return;
17
- A.current = !0;
11
+ let m = t(), h = c(), [g] = l(), _ = e((e) => e.setAuthTokens), v = e((e) => e.setAuthenticated), { exchangeOidcCode: y, validateOidcState: b, isExternalOidcCallback: x, handleExternalOidcCallback: S } = i(), [C, w] = s(null), [T, E] = s("processing"), [D, O] = s("unknown"), k = o(!1), A = o(null);
12
+ return a(() => {
13
+ if (k.current) return;
14
+ k.current = !0;
18
15
  let e = async () => {
19
16
  try {
20
- let e = _.get("error"), r = _.get("message") || _.get("error_description");
17
+ let e = g.get("error"), r = g.get("message") || g.get("error_description");
21
18
  if (e) throw Error(r || `SSO authentication failed: ${e}`);
22
- let i = _.get("token"), a = _.get("code"), o = _.get("state");
23
- if (i) k("saml"), await t(i);
24
- else if (a) k("oidc"), await n(a, o);
19
+ let i = g.get("token"), a = g.get("code"), o = g.get("state");
20
+ if (i) O("saml"), await t(i);
21
+ else if (a) O("oidc"), await n(a, o);
25
22
  else throw Error("Invalid SSO callback. No token or authorization code received.");
26
23
  } catch (e) {
27
- console.error("[SSO Callback] Error:", e), T(e instanceof Error ? e.message : "Failed to process SSO response"), D("error");
24
+ console.error("[SSO Callback] Error:", e), w(e instanceof Error ? e.message : "Failed to process SSO response"), E("error");
28
25
  }
29
26
  }, t = async (e) => {
30
- let t = _.get("refreshToken"), n = _.get("redirect"), r = _.get("requiresMFA") === "true", i = _.get("mfaChallengeId");
27
+ let t = g.get("refreshToken"), n = g.get("redirect"), r = g.get("requiresMFA") === "true", i = g.get("mfaChallengeId");
31
28
  if (r && i) {
32
- sessionStorage.setItem("mfa_challenge_id", i), sessionStorage.setItem("mfa_pending_token", e), sessionStorage.setItem("mfa_pending_refresh", t || ""), sessionStorage.setItem("mfa_redirect", n || "/dashboard"), g("/auth/mfa-verify", { replace: !0 });
29
+ sessionStorage.setItem("mfa_challenge_id", i), sessionStorage.setItem("mfa_pending_token", e), sessionStorage.setItem("mfa_pending_refresh", t || ""), sessionStorage.setItem("mfa_redirect", n || "/dashboard"), h("/auth/mfa-verify", { replace: !0 });
33
30
  return;
34
31
  }
35
- v(e, t || ""), y(!0), D("success"), j.current = setTimeout(() => {
36
- g(n || "/dashboard", { replace: !0 });
32
+ _(e, t || ""), v(!0), E("success"), A.current = setTimeout(() => {
33
+ h(n || "/dashboard", { replace: !0 });
37
34
  }, 1500);
38
35
  }, n = async (e, t) => {
39
36
  let n = sessionStorage.getItem("oidc_redirect");
40
- if (S()) {
37
+ if (x()) {
41
38
  if (!t) throw Error("State parameter is required for external OIDC flow");
42
- let n = await C(e, t);
39
+ let n = await S(e, t);
43
40
  if (!n.success) throw Error(n.error || "Failed to authenticate with external OIDC provider");
44
- D("success");
41
+ E("success");
45
42
  let r = n.redirectPath || "/dashboard";
46
- j.current = setTimeout(() => {
47
- g(r, { replace: !0 });
43
+ A.current = setTimeout(() => {
44
+ h(r, { replace: !0 });
48
45
  }, 1500);
49
46
  return;
50
47
  }
51
- if (t && !x(t)) throw Error("Invalid state parameter. This may be a CSRF attack. Please try logging in again.");
52
- let r = await b(e, sessionStorage.getItem("oidc_client_id") || "default-client", window.location.origin + "/auth/sso/callback");
48
+ if (t && !b(t)) throw Error("Invalid state parameter. This may be a CSRF attack. Please try logging in again.");
49
+ let r = await y(e, sessionStorage.getItem("oidc_client_id") || "default-client", window.location.origin + "/auth/sso/callback");
53
50
  if (!r.success) throw Error(r.error || "Failed to exchange authorization code");
54
51
  if (r.requiresMFA && r.mfaChallengeId) {
55
- sessionStorage.setItem("mfa_challenge_id", r.mfaChallengeId), sessionStorage.setItem("mfa_redirect", n || "/dashboard"), sessionStorage.removeItem("oidc_redirect"), sessionStorage.removeItem("oidc_client_id"), g("/auth/mfa-verify", { replace: !0 });
52
+ sessionStorage.setItem("mfa_challenge_id", r.mfaChallengeId), sessionStorage.setItem("mfa_redirect", n || "/dashboard"), sessionStorage.removeItem("oidc_redirect"), sessionStorage.removeItem("oidc_client_id"), h("/auth/mfa-verify", { replace: !0 });
56
53
  return;
57
54
  }
58
- D("success");
55
+ E("success");
59
56
  let i = n || "/dashboard";
60
- sessionStorage.removeItem("oidc_redirect"), sessionStorage.removeItem("oidc_client_id"), j.current = setTimeout(() => {
61
- g(i, { replace: !0 });
57
+ sessionStorage.removeItem("oidc_redirect"), sessionStorage.removeItem("oidc_client_id"), A.current = setTimeout(() => {
58
+ h(i, { replace: !0 });
62
59
  }, 1500);
63
60
  };
64
61
  e();
65
- let r = j;
62
+ let r = A;
66
63
  return () => {
67
64
  r.current &&= (clearTimeout(r.current), null);
68
65
  };
69
66
  }, [
70
- _,
71
67
  g,
68
+ h,
69
+ _,
72
70
  v,
73
71
  y,
74
72
  b,
75
73
  x,
76
- S,
77
- C
74
+ S
78
75
  ]), /* @__PURE__ */ d("div", {
79
76
  className: "min-h-screen flex items-center justify-center bg-bg-canvas p-4",
80
77
  children: /* @__PURE__ */ d("div", {
@@ -100,21 +97,21 @@ function m() {
100
97
  })
101
98
  })
102
99
  }),
103
- E === "processing" && /* @__PURE__ */ f(u, { children: [
100
+ T === "processing" && /* @__PURE__ */ f(u, { children: [
104
101
  /* @__PURE__ */ d("h1", {
105
102
  className: "text-2xl font-semibold text-text-primary mb-4",
106
- children: h("auth.ssoCallback.completing", "Completing Sign In")
103
+ children: m("auth.ssoCallback.completing", "Completing Sign In")
107
104
  }),
108
105
  /* @__PURE__ */ d("p", {
109
106
  className: "text-text-secondary mb-6",
110
- children: O === "oidc" ? h("auth.ssoCallback.exchangingCode", "Exchanging authorization code for access tokens...") : O === "saml" ? h("auth.ssoCallback.validatingSaml", "Validating your SAML credentials...") : h("auth.ssoCallback.validatingCreds", "Validating your credentials from your identity provider...")
107
+ children: D === "oidc" ? m("auth.ssoCallback.exchangingCode", "Exchanging authorization code for access tokens...") : D === "saml" ? m("auth.ssoCallback.validatingSaml", "Validating your SAML credentials...") : m("auth.ssoCallback.validatingCreds", "Validating your credentials from your identity provider...")
111
108
  }),
112
109
  /* @__PURE__ */ d("div", {
113
110
  className: "flex justify-center",
114
- children: /* @__PURE__ */ d(n, { className: "size-10 text-text-link animate-spin" })
111
+ children: /* @__PURE__ */ d(r, { className: "size-10 text-text-link animate-spin" })
115
112
  })
116
113
  ] }),
117
- E === "success" && /* @__PURE__ */ f(u, { children: [
114
+ T === "success" && /* @__PURE__ */ f(u, { children: [
118
115
  /* @__PURE__ */ d("div", {
119
116
  className: "size-16 mx-auto mb-4 rounded-full bg-status-success-bg-subtle flex items-center justify-center",
120
117
  children: /* @__PURE__ */ d("svg", {
@@ -132,59 +129,59 @@ function m() {
132
129
  }),
133
130
  /* @__PURE__ */ d("h1", {
134
131
  className: "text-2xl font-semibold text-text-primary mb-4",
135
- children: h("auth.ssoCallback.success", "Sign In Successful")
132
+ children: m("auth.ssoCallback.success", "Sign In Successful")
136
133
  }),
137
134
  /* @__PURE__ */ d("p", {
138
135
  className: "text-text-secondary mb-6",
139
- children: O === "oidc" ? h("auth.ssoCallback.oidcComplete", "OIDC authentication complete. Redirecting...") : O === "saml" ? h("auth.ssoCallback.samlComplete", "SAML authentication complete. Redirecting...") : h("auth.ssoCallback.authSuccess", "You have been authenticated successfully. Redirecting...")
136
+ children: D === "oidc" ? m("auth.ssoCallback.oidcComplete", "OIDC authentication complete. Redirecting...") : D === "saml" ? m("auth.ssoCallback.samlComplete", "SAML authentication complete. Redirecting...") : m("auth.ssoCallback.authSuccess", "You have been authenticated successfully. Redirecting...")
140
137
  }),
141
138
  /* @__PURE__ */ d("div", {
142
139
  className: "flex justify-center",
143
- children: /* @__PURE__ */ d(n, { className: "size-6 text-status-success-text animate-spin" })
140
+ children: /* @__PURE__ */ d(r, { className: "size-6 text-status-success-text animate-spin" })
144
141
  })
145
142
  ] }),
146
- E === "error" && /* @__PURE__ */ f(u, { children: [
143
+ T === "error" && /* @__PURE__ */ f(u, { children: [
147
144
  /* @__PURE__ */ d("div", {
148
145
  className: "size-16 mx-auto mb-4 rounded-full bg-status-error-bg-subtle flex items-center justify-center",
149
- children: /* @__PURE__ */ d(t, { className: "size-8 text-status-error-text" })
146
+ children: /* @__PURE__ */ d(n, { className: "size-8 text-status-error-text" })
150
147
  }),
151
148
  /* @__PURE__ */ d("h1", {
152
149
  className: "text-2xl font-semibold text-text-primary mb-4",
153
- children: h("auth.ssoCallback.failed", "Sign In Failed")
150
+ children: m("auth.ssoCallback.failed", "Sign In Failed")
154
151
  }),
155
152
  /* @__PURE__ */ d("p", {
156
153
  className: "text-status-error-text mb-6 text-sm",
157
- children: w
154
+ children: C
158
155
  }),
159
156
  /* @__PURE__ */ f("div", {
160
157
  className: "space-y-3",
161
158
  children: [/* @__PURE__ */ d(p, {
162
159
  type: "button",
163
- onClick: () => g("/auth/sso/login", { replace: !0 }),
160
+ onClick: () => h("/auth/sso/login", { replace: !0 }),
164
161
  variant: "default",
165
162
  size: "lg",
166
163
  className: "w-full font-semibold",
167
- children: h("auth.ssoCallback.tryAgain", "Try Again")
164
+ children: m("auth.ssoCallback.tryAgain", "Try Again")
168
165
  }), /* @__PURE__ */ d("button", {
169
166
  type: "button",
170
- onClick: () => g("/auth/login", { replace: !0 }),
167
+ onClick: () => h("/auth/login", { replace: !0 }),
171
168
  className: "w-full px-6 py-3 rounded-xl border border-border-default text-text-primary font-medium hover:bg-bg-sunken transition-colors",
172
- children: h("auth.ssoCallback.backToLogin", "Back to Login")
169
+ children: m("auth.ssoCallback.backToLogin", "Back to Login")
173
170
  })]
174
171
  })
175
172
  ] }),
176
- O !== "unknown" && /* @__PURE__ */ d("div", {
173
+ D !== "unknown" && /* @__PURE__ */ d("div", {
177
174
  className: "mt-6",
178
175
  children: /* @__PURE__ */ d("span", {
179
- className: `inline-flex items-center px-3 py-1 rounded-full text-xs font-medium ${O === "oidc" ? "bg-accent-purple-subtle text-accent-purple" : "bg-status-info-bg-subtle text-text-linkHover"}`,
180
- children: O === "oidc" ? "OpenID Connect" : "SAML 2.0"
176
+ className: `inline-flex items-center px-3 py-1 rounded-full text-xs font-medium ${D === "oidc" ? "bg-accent-purple-subtle text-accent-purple" : "bg-status-info-bg-subtle text-text-linkHover"}`,
177
+ children: D === "oidc" ? "OpenID Connect" : "SAML 2.0"
181
178
  })
182
179
  }),
183
180
  /* @__PURE__ */ d("div", {
184
181
  className: "mt-8 pt-6 border-t border-border-subtle",
185
182
  children: /* @__PURE__ */ d("p", {
186
183
  className: "text-xs text-text-secondary",
187
- children: h("auth.ssoCallback.helpText", "Having trouble? Contact your organization's IT administrator for assistance with SSO login.")
184
+ children: m("auth.ssoCallback.helpText", "Having trouble? Contact your organization's IT administrator for assistance with SSO login.")
188
185
  })
189
186
  })
190
187
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"SsoCallbackPage.js","names":[],"sources":["../../../src/auth/pages/SsoCallbackPage.tsx"],"sourcesContent":["import { useEffect, useState, useRef } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { useNavigate, useSearchParams } from \"react-router\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { useSsoOperations } from \"../hooks/useSsoOperations\";\nimport { Button } from \"@burdenoff/fe-libs/button\";\nimport { SpinnerIcon, AlertIcon } from \"../components/AuthIcons\";\n\n/**\n * SSO Callback Page\n * Handles redirects from:\n * - SAML ACS server (with token/refreshToken in query params)\n * - OIDC Authorization Server (with code/state in query params)\n */\nexport function SsoCallbackPage() {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n\n const navigate = useNavigate();\n const [searchParams] = useSearchParams();\n const setAuthTokens = useAuthStore((state) => state.setAuthTokens);\n const setAuthenticated = useAuthStore((state) => state.setAuthenticated);\n const {\n exchangeOidcCode,\n validateOidcState,\n isExternalOidcCallback,\n handleExternalOidcCallback,\n } = useSsoOperations();\n\n const [error, setError] = useState<string | null>(null);\n const [status, setStatus] = useState<\"processing\" | \"success\" | \"error\">(\n \"processing\",\n );\n const [callbackType, setCallbackType] = useState<\"saml\" | \"oidc\" | \"unknown\">(\n \"unknown\",\n );\n const processedRef = useRef(false);\n const redirectTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n // Prevent double execution in React StrictMode\n if (processedRef.current) {\n return;\n }\n processedRef.current = true;\n\n const processCallback = async () => {\n try {\n // Check for error from either SSO provider\n const errorParam = searchParams.get(\"error\");\n const errorMessage =\n searchParams.get(\"message\") || searchParams.get(\"error_description\");\n\n if (errorParam) {\n throw new Error(\n errorMessage || `SSO authentication failed: ${errorParam}`,\n );\n }\n\n // Detect callback type based on query parameters\n const token = searchParams.get(\"token\");\n const code = searchParams.get(\"code\");\n const state = searchParams.get(\"state\");\n\n if (token) {\n // SAML callback - token already exchanged by ACS server\n setCallbackType(\"saml\");\n await handleSamlCallback(token);\n } else if (code) {\n // OIDC callback - need to exchange code for tokens\n setCallbackType(\"oidc\");\n await handleOidcCallback(code, state);\n } else {\n throw new Error(\n \"Invalid SSO callback. No token or authorization code received.\",\n );\n }\n } catch (err) {\n console.error(\"[SSO Callback] Error:\", err);\n setError(\n err instanceof Error ? err.message : \"Failed to process SSO response\",\n );\n setStatus(\"error\");\n }\n };\n\n /**\n * Handle SAML callback from ACS server\n * Token is already exchanged, just store and redirect\n * Also handles MFA if required\n */\n const handleSamlCallback = async (token: string) => {\n const refreshToken = searchParams.get(\"refreshToken\");\n const redirect = searchParams.get(\"redirect\");\n const requiresMFA = searchParams.get(\"requiresMFA\") === \"true\";\n const mfaChallengeId = searchParams.get(\"mfaChallengeId\");\n\n // Check if MFA is required\n if (requiresMFA && mfaChallengeId) {\n // Store partial auth state and MFA info\n sessionStorage.setItem(\"mfa_challenge_id\", mfaChallengeId);\n sessionStorage.setItem(\"mfa_pending_token\", token);\n sessionStorage.setItem(\"mfa_pending_refresh\", refreshToken || \"\");\n sessionStorage.setItem(\"mfa_redirect\", redirect || \"/dashboard\");\n\n // Redirect to MFA verification\n navigate(\"/auth/mfa-verify\", { replace: true });\n return;\n }\n\n // Store tokens\n setAuthTokens(token, refreshToken || \"\");\n setAuthenticated(true);\n\n setStatus(\"success\");\n\n // Redirect to the specified URL or default to dashboard\n redirectTimerRef.current = setTimeout(() => {\n const redirectUrl = redirect || \"/dashboard\";\n navigate(redirectUrl, { replace: true });\n }, 1500);\n };\n\n /**\n * Handle OIDC callback - exchange code for tokens\n */\n const handleOidcCallback = async (code: string, state: string | null) => {\n // Cache the OIDC redirect target — used in both the MFA branch and the\n // success branch, plus removed once at the end of the flow.\n const oidcRedirect = sessionStorage.getItem(\"oidc_redirect\");\n const isExternal = isExternalOidcCallback();\n\n // Check if this is an external OIDC flow (Azure AD, Google, etc.)\n if (isExternal) {\n if (!state) {\n throw new Error(\"State parameter is required for external OIDC flow\");\n }\n\n // Use the external OIDC handler\n const result = await handleExternalOidcCallback(code, state);\n\n if (!result.success) {\n throw new Error(\n result.error ||\n \"Failed to authenticate with external OIDC provider\",\n );\n }\n\n setStatus(\"success\");\n\n // Redirect after brief success message using the redirect path from result\n const redirectUrl = result.redirectPath || \"/dashboard\";\n redirectTimerRef.current = setTimeout(() => {\n navigate(redirectUrl, { replace: true });\n }, 1500);\n return;\n }\n\n // Internal OIDC flow (our auth service as provider)\n // Validate state to prevent CSRF\n if (state && !validateOidcState(state)) {\n throw new Error(\n \"Invalid state parameter. This may be a CSRF attack. Please try logging in again.\",\n );\n }\n\n // Get client configuration from session storage\n const clientId =\n sessionStorage.getItem(\"oidc_client_id\") || \"default-client\";\n const redirectUri = window.location.origin + \"/auth/sso/callback\";\n\n // Exchange code for tokens\n const result = await exchangeOidcCode(code, clientId, redirectUri);\n\n if (!result.success) {\n throw new Error(\n result.error || \"Failed to exchange authorization code\",\n );\n }\n\n // Check if MFA is required after OIDC authentication\n if (result.requiresMFA && result.mfaChallengeId) {\n // Store MFA challenge info for the verification page\n sessionStorage.setItem(\"mfa_challenge_id\", result.mfaChallengeId);\n sessionStorage.setItem(\"mfa_redirect\", oidcRedirect || \"/dashboard\");\n\n // Clean up OIDC session data\n sessionStorage.removeItem(\"oidc_redirect\");\n sessionStorage.removeItem(\"oidc_client_id\");\n\n // Redirect to MFA verification\n navigate(\"/auth/mfa-verify\", { replace: true });\n return;\n }\n\n setStatus(\"success\");\n\n // Get redirect URL from state or session storage\n const redirect = oidcRedirect || \"/dashboard\";\n sessionStorage.removeItem(\"oidc_redirect\");\n sessionStorage.removeItem(\"oidc_client_id\");\n\n // Redirect after brief success message\n redirectTimerRef.current = setTimeout(() => {\n navigate(redirect, { replace: true });\n }, 1500);\n };\n\n processCallback();\n const timerRefCapture = redirectTimerRef;\n return () => {\n if (timerRefCapture.current) {\n clearTimeout(timerRefCapture.current);\n timerRefCapture.current = null;\n }\n };\n }, [\n searchParams,\n navigate,\n setAuthTokens,\n setAuthenticated,\n exchangeOidcCode,\n validateOidcState,\n isExternalOidcCallback,\n handleExternalOidcCallback,\n ]);\n\n return (\n <div className=\"min-h-screen flex items-center justify-center bg-bg-canvas p-4\">\n <div className=\"w-full max-w-md\">\n <div className=\"bg-bg-surface rounded-2xl shadow-xl p-8 text-center\">\n {/* Logo */}\n <div className=\"mb-6\">\n <div className=\"auth-brand-badge mx-auto flex size-16 items-center justify-center rounded-xl\">\n <svg\n className=\"size-8 text-white\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z\"\n />\n </svg>\n </div>\n </div>\n\n {/* Processing State */}\n {status === \"processing\" && (\n <>\n <h1 className=\"text-2xl font-semibold text-text-primary mb-4\">\n {tr(\"auth.ssoCallback.completing\", \"Completing Sign In\")}\n </h1>\n <p className=\"text-text-secondary mb-6\">\n {callbackType === \"oidc\"\n ? tr(\n \"auth.ssoCallback.exchangingCode\",\n \"Exchanging authorization code for access tokens...\",\n )\n : callbackType === \"saml\"\n ? tr(\n \"auth.ssoCallback.validatingSaml\",\n \"Validating your SAML credentials...\",\n )\n : tr(\n \"auth.ssoCallback.validatingCreds\",\n \"Validating your credentials from your identity provider...\",\n )}\n </p>\n <div className=\"flex justify-center\">\n <SpinnerIcon className=\"size-10 text-text-link animate-spin\" />\n </div>\n </>\n )}\n\n {/* Success State */}\n {status === \"success\" && (\n <>\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-status-success-bg-subtle flex items-center justify-center\">\n <svg\n className=\"size-8 text-status-success-text\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n </div>\n <h1 className=\"text-2xl font-semibold text-text-primary mb-4\">\n {tr(\"auth.ssoCallback.success\", \"Sign In Successful\")}\n </h1>\n <p className=\"text-text-secondary mb-6\">\n {callbackType === \"oidc\"\n ? tr(\n \"auth.ssoCallback.oidcComplete\",\n \"OIDC authentication complete. Redirecting...\",\n )\n : callbackType === \"saml\"\n ? tr(\n \"auth.ssoCallback.samlComplete\",\n \"SAML authentication complete. Redirecting...\",\n )\n : tr(\n \"auth.ssoCallback.authSuccess\",\n \"You have been authenticated successfully. Redirecting...\",\n )}\n </p>\n <div className=\"flex justify-center\">\n <SpinnerIcon className=\"size-6 text-status-success-text animate-spin\" />\n </div>\n </>\n )}\n\n {/* Error State */}\n {status === \"error\" && (\n <>\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-status-error-bg-subtle flex items-center justify-center\">\n <AlertIcon className=\"size-8 text-status-error-text\" />\n </div>\n <h1 className=\"text-2xl font-semibold text-text-primary mb-4\">\n {tr(\"auth.ssoCallback.failed\", \"Sign In Failed\")}\n </h1>\n <p className=\"text-status-error-text mb-6 text-sm\">{error}</p>\n <div className=\"space-y-3\">\n <Button\n type=\"button\"\n onClick={() => navigate(\"/auth/sso/login\", { replace: true })}\n variant=\"default\"\n size=\"lg\"\n className=\"w-full font-semibold\"\n >\n {tr(\"auth.ssoCallback.tryAgain\", \"Try Again\")}\n </Button>\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/login\", { replace: true })}\n className=\"w-full px-6 py-3 rounded-xl border border-border-default text-text-primary font-medium hover:bg-bg-sunken transition-colors\"\n >\n {tr(\"auth.ssoCallback.backToLogin\", \"Back to Login\")}\n </button>\n </div>\n </>\n )}\n\n {/* SSO Type Badge */}\n {callbackType !== \"unknown\" && (\n <div className=\"mt-6\">\n <span\n className={`inline-flex items-center px-3 py-1 rounded-full text-xs font-medium ${\n callbackType === \"oidc\"\n ? \"bg-accent-purple-subtle text-accent-purple\"\n : \"bg-status-info-bg-subtle text-text-linkHover\"\n }`}\n >\n {callbackType === \"oidc\" ? \"OpenID Connect\" : \"SAML 2.0\"}\n </span>\n </div>\n )}\n\n {/* Help Text */}\n <div className=\"mt-8 pt-6 border-t border-border-subtle\">\n <p className=\"text-xs text-text-secondary\">\n {tr(\n \"auth.ssoCallback.helpText\",\n \"Having trouble? Contact your organization's IT administrator for assistance with SSO login.\",\n )}\n </p>\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SsoCallbackPage.js","names":[],"sources":["../../../src/auth/pages/SsoCallbackPage.tsx"],"sourcesContent":["import { useEffect, useState, useRef } from \"react\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport { useNavigate, useSearchParams } from \"react-router\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { useSsoOperations } from \"../hooks/useSsoOperations\";\nimport { Button } from \"@burdenoff/fe-libs/button\";\nimport { SpinnerIcon, AlertIcon } from \"../components/AuthIcons\";\n\n/**\n * SSO Callback Page\n * Handles redirects from:\n * - SAML ACS server (with token/refreshToken in query params)\n * - OIDC Authorization Server (with code/state in query params)\n */\nexport function SsoCallbackPage() {\n const tr = useTr();\n\n const navigate = useNavigate();\n const [searchParams] = useSearchParams();\n const setAuthTokens = useAuthStore((state) => state.setAuthTokens);\n const setAuthenticated = useAuthStore((state) => state.setAuthenticated);\n const {\n exchangeOidcCode,\n validateOidcState,\n isExternalOidcCallback,\n handleExternalOidcCallback,\n } = useSsoOperations();\n\n const [error, setError] = useState<string | null>(null);\n const [status, setStatus] = useState<\"processing\" | \"success\" | \"error\">(\n \"processing\",\n );\n const [callbackType, setCallbackType] = useState<\"saml\" | \"oidc\" | \"unknown\">(\n \"unknown\",\n );\n const processedRef = useRef(false);\n const redirectTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n // Prevent double execution in React StrictMode\n if (processedRef.current) {\n return;\n }\n processedRef.current = true;\n\n const processCallback = async () => {\n try {\n // Check for error from either SSO provider\n const errorParam = searchParams.get(\"error\");\n const errorMessage =\n searchParams.get(\"message\") || searchParams.get(\"error_description\");\n\n if (errorParam) {\n throw new Error(\n errorMessage || `SSO authentication failed: ${errorParam}`,\n );\n }\n\n // Detect callback type based on query parameters\n const token = searchParams.get(\"token\");\n const code = searchParams.get(\"code\");\n const state = searchParams.get(\"state\");\n\n if (token) {\n // SAML callback - token already exchanged by ACS server\n setCallbackType(\"saml\");\n await handleSamlCallback(token);\n } else if (code) {\n // OIDC callback - need to exchange code for tokens\n setCallbackType(\"oidc\");\n await handleOidcCallback(code, state);\n } else {\n throw new Error(\n \"Invalid SSO callback. No token or authorization code received.\",\n );\n }\n } catch (err) {\n console.error(\"[SSO Callback] Error:\", err);\n setError(\n err instanceof Error ? err.message : \"Failed to process SSO response\",\n );\n setStatus(\"error\");\n }\n };\n\n /**\n * Handle SAML callback from ACS server\n * Token is already exchanged, just store and redirect\n * Also handles MFA if required\n */\n const handleSamlCallback = async (token: string) => {\n const refreshToken = searchParams.get(\"refreshToken\");\n const redirect = searchParams.get(\"redirect\");\n const requiresMFA = searchParams.get(\"requiresMFA\") === \"true\";\n const mfaChallengeId = searchParams.get(\"mfaChallengeId\");\n\n // Check if MFA is required\n if (requiresMFA && mfaChallengeId) {\n // Store partial auth state and MFA info\n sessionStorage.setItem(\"mfa_challenge_id\", mfaChallengeId);\n sessionStorage.setItem(\"mfa_pending_token\", token);\n sessionStorage.setItem(\"mfa_pending_refresh\", refreshToken || \"\");\n sessionStorage.setItem(\"mfa_redirect\", redirect || \"/dashboard\");\n\n // Redirect to MFA verification\n navigate(\"/auth/mfa-verify\", { replace: true });\n return;\n }\n\n // Store tokens\n setAuthTokens(token, refreshToken || \"\");\n setAuthenticated(true);\n\n setStatus(\"success\");\n\n // Redirect to the specified URL or default to dashboard\n redirectTimerRef.current = setTimeout(() => {\n const redirectUrl = redirect || \"/dashboard\";\n navigate(redirectUrl, { replace: true });\n }, 1500);\n };\n\n /**\n * Handle OIDC callback - exchange code for tokens\n */\n const handleOidcCallback = async (code: string, state: string | null) => {\n // Cache the OIDC redirect target — used in both the MFA branch and the\n // success branch, plus removed once at the end of the flow.\n const oidcRedirect = sessionStorage.getItem(\"oidc_redirect\");\n const isExternal = isExternalOidcCallback();\n\n // Check if this is an external OIDC flow (Azure AD, Google, etc.)\n if (isExternal) {\n if (!state) {\n throw new Error(\"State parameter is required for external OIDC flow\");\n }\n\n // Use the external OIDC handler\n const result = await handleExternalOidcCallback(code, state);\n\n if (!result.success) {\n throw new Error(\n result.error ||\n \"Failed to authenticate with external OIDC provider\",\n );\n }\n\n setStatus(\"success\");\n\n // Redirect after brief success message using the redirect path from result\n const redirectUrl = result.redirectPath || \"/dashboard\";\n redirectTimerRef.current = setTimeout(() => {\n navigate(redirectUrl, { replace: true });\n }, 1500);\n return;\n }\n\n // Internal OIDC flow (our auth service as provider)\n // Validate state to prevent CSRF\n if (state && !validateOidcState(state)) {\n throw new Error(\n \"Invalid state parameter. This may be a CSRF attack. Please try logging in again.\",\n );\n }\n\n // Get client configuration from session storage\n const clientId =\n sessionStorage.getItem(\"oidc_client_id\") || \"default-client\";\n const redirectUri = window.location.origin + \"/auth/sso/callback\";\n\n // Exchange code for tokens\n const result = await exchangeOidcCode(code, clientId, redirectUri);\n\n if (!result.success) {\n throw new Error(\n result.error || \"Failed to exchange authorization code\",\n );\n }\n\n // Check if MFA is required after OIDC authentication\n if (result.requiresMFA && result.mfaChallengeId) {\n // Store MFA challenge info for the verification page\n sessionStorage.setItem(\"mfa_challenge_id\", result.mfaChallengeId);\n sessionStorage.setItem(\"mfa_redirect\", oidcRedirect || \"/dashboard\");\n\n // Clean up OIDC session data\n sessionStorage.removeItem(\"oidc_redirect\");\n sessionStorage.removeItem(\"oidc_client_id\");\n\n // Redirect to MFA verification\n navigate(\"/auth/mfa-verify\", { replace: true });\n return;\n }\n\n setStatus(\"success\");\n\n // Get redirect URL from state or session storage\n const redirect = oidcRedirect || \"/dashboard\";\n sessionStorage.removeItem(\"oidc_redirect\");\n sessionStorage.removeItem(\"oidc_client_id\");\n\n // Redirect after brief success message\n redirectTimerRef.current = setTimeout(() => {\n navigate(redirect, { replace: true });\n }, 1500);\n };\n\n processCallback();\n const timerRefCapture = redirectTimerRef;\n return () => {\n if (timerRefCapture.current) {\n clearTimeout(timerRefCapture.current);\n timerRefCapture.current = null;\n }\n };\n }, [\n searchParams,\n navigate,\n setAuthTokens,\n setAuthenticated,\n exchangeOidcCode,\n validateOidcState,\n isExternalOidcCallback,\n handleExternalOidcCallback,\n ]);\n\n return (\n <div className=\"min-h-screen flex items-center justify-center bg-bg-canvas p-4\">\n <div className=\"w-full max-w-md\">\n <div className=\"bg-bg-surface rounded-2xl shadow-xl p-8 text-center\">\n {/* Logo */}\n <div className=\"mb-6\">\n <div className=\"auth-brand-badge mx-auto flex size-16 items-center justify-center rounded-xl\">\n <svg\n className=\"size-8 text-white\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z\"\n />\n </svg>\n </div>\n </div>\n\n {/* Processing State */}\n {status === \"processing\" && (\n <>\n <h1 className=\"text-2xl font-semibold text-text-primary mb-4\">\n {tr(\"auth.ssoCallback.completing\", \"Completing Sign In\")}\n </h1>\n <p className=\"text-text-secondary mb-6\">\n {callbackType === \"oidc\"\n ? tr(\n \"auth.ssoCallback.exchangingCode\",\n \"Exchanging authorization code for access tokens...\",\n )\n : callbackType === \"saml\"\n ? tr(\n \"auth.ssoCallback.validatingSaml\",\n \"Validating your SAML credentials...\",\n )\n : tr(\n \"auth.ssoCallback.validatingCreds\",\n \"Validating your credentials from your identity provider...\",\n )}\n </p>\n <div className=\"flex justify-center\">\n <SpinnerIcon className=\"size-10 text-text-link animate-spin\" />\n </div>\n </>\n )}\n\n {/* Success State */}\n {status === \"success\" && (\n <>\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-status-success-bg-subtle flex items-center justify-center\">\n <svg\n className=\"size-8 text-status-success-text\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n </div>\n <h1 className=\"text-2xl font-semibold text-text-primary mb-4\">\n {tr(\"auth.ssoCallback.success\", \"Sign In Successful\")}\n </h1>\n <p className=\"text-text-secondary mb-6\">\n {callbackType === \"oidc\"\n ? tr(\n \"auth.ssoCallback.oidcComplete\",\n \"OIDC authentication complete. Redirecting...\",\n )\n : callbackType === \"saml\"\n ? tr(\n \"auth.ssoCallback.samlComplete\",\n \"SAML authentication complete. Redirecting...\",\n )\n : tr(\n \"auth.ssoCallback.authSuccess\",\n \"You have been authenticated successfully. Redirecting...\",\n )}\n </p>\n <div className=\"flex justify-center\">\n <SpinnerIcon className=\"size-6 text-status-success-text animate-spin\" />\n </div>\n </>\n )}\n\n {/* Error State */}\n {status === \"error\" && (\n <>\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-status-error-bg-subtle flex items-center justify-center\">\n <AlertIcon className=\"size-8 text-status-error-text\" />\n </div>\n <h1 className=\"text-2xl font-semibold text-text-primary mb-4\">\n {tr(\"auth.ssoCallback.failed\", \"Sign In Failed\")}\n </h1>\n <p className=\"text-status-error-text mb-6 text-sm\">{error}</p>\n <div className=\"space-y-3\">\n <Button\n type=\"button\"\n onClick={() => navigate(\"/auth/sso/login\", { replace: true })}\n variant=\"default\"\n size=\"lg\"\n className=\"w-full font-semibold\"\n >\n {tr(\"auth.ssoCallback.tryAgain\", \"Try Again\")}\n </Button>\n <button\n type=\"button\"\n onClick={() => navigate(\"/auth/login\", { replace: true })}\n className=\"w-full px-6 py-3 rounded-xl border border-border-default text-text-primary font-medium hover:bg-bg-sunken transition-colors\"\n >\n {tr(\"auth.ssoCallback.backToLogin\", \"Back to Login\")}\n </button>\n </div>\n </>\n )}\n\n {/* SSO Type Badge */}\n {callbackType !== \"unknown\" && (\n <div className=\"mt-6\">\n <span\n className={`inline-flex items-center px-3 py-1 rounded-full text-xs font-medium ${\n callbackType === \"oidc\"\n ? \"bg-accent-purple-subtle text-accent-purple\"\n : \"bg-status-info-bg-subtle text-text-linkHover\"\n }`}\n >\n {callbackType === \"oidc\" ? \"OpenID Connect\" : \"SAML 2.0\"}\n </span>\n </div>\n )}\n\n {/* Help Text */}\n <div className=\"mt-8 pt-6 border-t border-border-subtle\">\n <p className=\"text-xs text-text-secondary\">\n {tr(\n \"auth.ssoCallback.helpText\",\n \"Having trouble? Contact your organization's IT administrator for assistance with SSO login.\",\n )}\n </p>\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAcA,SAAgB,IAAkB;CAChC,IAAM,IAAK,GAAO,EAEZ,IAAW,GAAa,EACxB,CAAC,KAAgB,GAAiB,EAClC,IAAgB,GAAc,MAAU,EAAM,cAAc,EAC5D,IAAmB,GAAc,MAAU,EAAM,iBAAiB,EAClE,EACJ,qBACA,sBACA,2BACA,kCACE,GAAkB,EAEhB,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,CAAC,GAAQ,KAAa,EAC1B,aACD,EACK,CAAC,GAAc,KAAmB,EACtC,UACD,EACK,IAAe,EAAO,GAAM,EAC5B,IAAmB,EAA6C,KAAK;AA8L3E,QA5LA,QAAgB;AAEd,MAAI,EAAa,QACf;AAEF,IAAa,UAAU;EAEvB,IAAM,IAAkB,YAAY;AAClC,OAAI;IAEF,IAAM,IAAa,EAAa,IAAI,QAAQ,EACtC,IACJ,EAAa,IAAI,UAAU,IAAI,EAAa,IAAI,oBAAoB;AAEtE,QAAI,EACF,OAAU,MACR,KAAgB,8BAA8B,IAC/C;IAIH,IAAM,IAAQ,EAAa,IAAI,QAAQ,EACjC,IAAO,EAAa,IAAI,OAAO,EAC/B,IAAQ,EAAa,IAAI,QAAQ;AAEvC,QAAI,EAGF,CADA,EAAgB,OAAO,EACvB,MAAM,EAAmB,EAAM;aACtB,EAGT,CADA,EAAgB,OAAO,EACvB,MAAM,EAAmB,GAAM,EAAM;QAErC,OAAU,MACR,iEACD;YAEI,GAAK;AAKZ,IAJA,QAAQ,MAAM,yBAAyB,EAAI,EAC3C,EACE,aAAe,QAAQ,EAAI,UAAU,iCACtC,EACD,EAAU,QAAQ;;KAShB,IAAqB,OAAO,MAAkB;GAClD,IAAM,IAAe,EAAa,IAAI,eAAe,EAC/C,IAAW,EAAa,IAAI,WAAW,EACvC,IAAc,EAAa,IAAI,cAAc,KAAK,QAClD,IAAiB,EAAa,IAAI,iBAAiB;AAGzD,OAAI,KAAe,GAAgB;AAQjC,IANA,eAAe,QAAQ,oBAAoB,EAAe,EAC1D,eAAe,QAAQ,qBAAqB,EAAM,EAClD,eAAe,QAAQ,uBAAuB,KAAgB,GAAG,EACjE,eAAe,QAAQ,gBAAgB,KAAY,aAAa,EAGhE,EAAS,oBAAoB,EAAE,SAAS,IAAM,CAAC;AAC/C;;AAUF,GANA,EAAc,GAAO,KAAgB,GAAG,EACxC,EAAiB,GAAK,EAEtB,EAAU,UAAU,EAGpB,EAAiB,UAAU,iBAAiB;AAE1C,MADoB,KAAY,cACV,EAAE,SAAS,IAAM,CAAC;MACvC,KAAK;KAMJ,IAAqB,OAAO,GAAc,MAAyB;GAGvE,IAAM,IAAe,eAAe,QAAQ,gBAAgB;AAI5D,OAHmB,GAAwB,EAG3B;AACd,QAAI,CAAC,EACH,OAAU,MAAM,qDAAqD;IAIvE,IAAM,IAAS,MAAM,EAA2B,GAAM,EAAM;AAE5D,QAAI,CAAC,EAAO,QACV,OAAU,MACR,EAAO,SACL,qDACH;AAGH,MAAU,UAAU;IAGpB,IAAM,IAAc,EAAO,gBAAgB;AAC3C,MAAiB,UAAU,iBAAiB;AAC1C,OAAS,GAAa,EAAE,SAAS,IAAM,CAAC;OACvC,KAAK;AACR;;AAKF,OAAI,KAAS,CAAC,EAAkB,EAAM,CACpC,OAAU,MACR,mFACD;GASH,IAAM,IAAS,MAAM,EAAiB,GAJpC,eAAe,QAAQ,iBAAiB,IAAI,kBAC1B,OAAO,SAAS,SAAS,qBAGqB;AAElE,OAAI,CAAC,EAAO,QACV,OAAU,MACR,EAAO,SAAS,wCACjB;AAIH,OAAI,EAAO,eAAe,EAAO,gBAAgB;AAU/C,IARA,eAAe,QAAQ,oBAAoB,EAAO,eAAe,EACjE,eAAe,QAAQ,gBAAgB,KAAgB,aAAa,EAGpE,eAAe,WAAW,gBAAgB,EAC1C,eAAe,WAAW,iBAAiB,EAG3C,EAAS,oBAAoB,EAAE,SAAS,IAAM,CAAC;AAC/C;;AAGF,KAAU,UAAU;GAGpB,IAAM,IAAW,KAAgB;AAKjC,GAJA,eAAe,WAAW,gBAAgB,EAC1C,eAAe,WAAW,iBAAiB,EAG3C,EAAiB,UAAU,iBAAiB;AAC1C,MAAS,GAAU,EAAE,SAAS,IAAM,CAAC;MACpC,KAAK;;AAGV,KAAiB;EACjB,IAAM,IAAkB;AACxB,eAAa;AACX,GAEE,EAAgB,aADhB,aAAa,EAAgB,QAAQ,EACX;;IAG7B;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,EAGA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cAAf;KAEE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,OAAD;QACE,WAAU;QACV,MAAK;QACL,SAAQ;QACR,QAAO;kBAEP,kBAAC,QAAD;SACE,eAAc;SACd,gBAAe;SACf,aAAa;SACb,GAAE;SACF,CAAA;QACE,CAAA;OACF,CAAA;MACF,CAAA;KAGL,MAAW,gBACV,kBAAA,GAAA,EAAA,UAAA;MACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,+BAA+B,qBAAqB;OACrD,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBACV,MAAiB,SACd,EACE,mCACA,qDACD,GACD,MAAiB,SACf,EACE,mCACA,sCACD,GACD,EACE,oCACA,6DACD;OACL,CAAA;MACJ,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAa,WAAU,uCAAwC,CAAA;OAC3D,CAAA;MACL,EAAA,CAAA;KAIJ,MAAW,aACV,kBAAA,GAAA,EAAA,UAAA;MACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,OAAD;QACE,WAAU;QACV,MAAK;QACL,SAAQ;QACR,QAAO;kBAEP,kBAAC,QAAD;SACE,eAAc;SACd,gBAAe;SACf,aAAa;SACb,GAAE;SACF,CAAA;QACE,CAAA;OACF,CAAA;MACN,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,4BAA4B,qBAAqB;OAClD,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBACV,MAAiB,SACd,EACE,iCACA,+CACD,GACD,MAAiB,SACf,EACE,iCACA,+CACD,GACD,EACE,gCACA,2DACD;OACL,CAAA;MACJ,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAa,WAAU,gDAAiD,CAAA;OACpE,CAAA;MACL,EAAA,CAAA;KAIJ,MAAW,WACV,kBAAA,GAAA,EAAA,UAAA;MACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAW,WAAU,iCAAkC,CAAA;OACnD,CAAA;MACN,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,2BAA2B,iBAAiB;OAC7C,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBAAuC;OAAU,CAAA;MAC9D,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QACE,MAAK;QACL,eAAe,EAAS,mBAAmB,EAAE,SAAS,IAAM,CAAC;QAC7D,SAAQ;QACR,MAAK;QACL,WAAU;kBAET,EAAG,6BAA6B,YAAY;QACtC,CAAA,EACT,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAS,eAAe,EAAE,SAAS,IAAM,CAAC;QACzD,WAAU;kBAET,EAAG,gCAAgC,gBAAgB;QAC7C,CAAA,CACL;;MACL,EAAA,CAAA;KAIJ,MAAiB,aAChB,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,QAAD;OACE,WAAW,uEACT,MAAiB,SACb,+CACA;iBAGL,MAAiB,SAAS,mBAAmB;OACzC,CAAA;MACH,CAAA;KAIR,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,6BACA,8FACD;OACC,CAAA;MACA,CAAA;KACF;;GACF,CAAA;EACF,CAAA"}