@burdenoff/microfe-auth 2026.511.1 → 2026.514.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/AuthRoutes.d.ts.map +1 -1
- package/dist/auth/AuthRoutes.js +94 -58
- package/dist/auth/AuthRoutes.js.map +1 -1
- package/dist/auth/components/AuthLayout.js +1 -1
- package/dist/auth/components/AuthLayout.js.map +1 -1
- package/dist/auth/hooks/useApiKeyOperations.d.ts +5 -1
- package/dist/auth/hooks/useApiKeyOperations.d.ts.map +1 -1
- package/dist/auth/hooks/useAppOperations.d.ts +5 -1
- package/dist/auth/hooks/useAppOperations.d.ts.map +1 -1
- package/dist/auth/hooks/useDelegationOperations.d.ts +10 -2
- package/dist/auth/hooks/useDelegationOperations.d.ts.map +1 -1
- package/dist/auth/hooks/useSessionOperations.js +1 -1
- package/dist/auth/hooks/useSessionOperations.js.map +1 -1
- package/dist/auth/pages/AuditLogPage.d.ts +8 -1
- package/dist/auth/pages/AuditLogPage.d.ts.map +1 -1
- package/dist/auth/pages/AuditLogPage.js +189 -104
- package/dist/auth/pages/AuditLogPage.js.map +1 -1
- package/dist/auth/pages/MfaSetupPage.d.ts.map +1 -1
- package/dist/auth/pages/MfaSetupPage.js +5 -5
- package/dist/auth/pages/MfaSetupPage.js.map +1 -1
- package/dist/auth/pages/SecurityPage.d.ts.map +1 -1
- package/dist/auth/pages/SecurityPage.js +335 -267
- package/dist/auth/pages/SecurityPage.js.map +1 -1
- package/dist/auth/pages/SessionsPage.d.ts +6 -0
- package/dist/auth/pages/SessionsPage.d.ts.map +1 -1
- package/dist/auth/pages/SessionsPage.js +255 -143
- package/dist/auth/pages/SessionsPage.js.map +1 -1
- package/dist/auth/pages/UserDetailsPage.d.ts.map +1 -1
- package/dist/auth/pages/UserDetailsPage.js +1 -1
- package/dist/auth/pages/UserDetailsPage.js.map +1 -1
- package/dist/auth/pages/UsersPage.d.ts.map +1 -1
- package/dist/auth/pages/UsersPage.js +501 -378
- package/dist/auth/pages/UsersPage.js.map +1 -1
- package/dist/generated/global-operations.d.ts +219 -103
- package/dist/generated/global-operations.d.ts.map +1 -1
- package/dist/generated/global-operations.js +237 -166
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/global-types.d.ts +2544 -270
- package/dist/generated/global-types.d.ts.map +1 -1
- package/dist/generated/wspace-operations.d.ts +41 -1
- package/dist/generated/wspace-operations.d.ts.map +1 -1
- package/dist/generated/wspace-operations.js +26 -0
- package/dist/generated/wspace-operations.js.map +1 -0
- package/dist/generated/wspace-types.d.ts +2974 -337
- package/dist/generated/wspace-types.d.ts.map +1 -1
- package/dist/microfe-auth.css +1 -1
- package/dist/translations/en.d.ts +249 -0
- package/dist/translations/en.d.ts.map +1 -0
- package/package.json +1 -1
- package/dist/auth/pages/ApiKeysPage.d.ts +0 -5
- package/dist/auth/pages/ApiKeysPage.d.ts.map +0 -1
- package/dist/auth/pages/DelegationsPage.d.ts +0 -5
- package/dist/auth/pages/DelegationsPage.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSessionOperations.js","names":[],"sources":["../../../src/auth/hooks/useSessionOperations.ts"],"sourcesContent":["import { useQuery, useMutation } from \"@apollo/client/react\";\nimport { useAuthStore } from \"../store/authStore\";\nimport {\n TokenInfoDocument,\n ValidateTokensDocument,\n ActiveSessionsDocument,\n RevokeSessionDocument,\n RevokeAllSessionsDocument,\n} from \"../../generated/global-operations\";\nimport type {\n ActiveSessionsQuery,\n RevokeSessionMutation,\n RevokeSessionMutationVariables,\n RevokeAllSessionsMutation,\n RevokeAllSessionsMutationVariables,\n} from \"../../generated/global-operations\";\nimport type { TokenValidationResponse, ValidateTokensInput } from \"../types\";\n\n// =============================================================================\n// GRAPHQL RESPONSE INTERFACES\n// =============================================================================\n\ninterface TokenInfoQueryResponse {\n tokenInfo: TokenValidationResponse;\n}\n\ninterface ValidateTokensMutationResponse {\n validateTokens: TokenValidationResponse;\n}\n\ninterface ValidateTokensMutationVariables {\n input: ValidateTokensInput;\n}\n\ninterface UseSessionOperationsOptions {\n disableTokenInfoQuery?: boolean;\n}\n\n/**\n * Hook for session/token management operations\n */\nexport function useSessionOperations(\n options: UseSessionOperationsOptions = {},\n) {\n const store = useAuthStore();\n const disableTokenInfoQuery = options.disableTokenInfoQuery ?? false;\n const [validateTokensMutation] = useMutation<\n ValidateTokensMutationResponse,\n ValidateTokensMutationVariables\n >(ValidateTokensDocument);\n\n const { data, loading, refetch } = useQuery<TokenInfoQueryResponse>(\n TokenInfoDocument,\n {\n skip: disableTokenInfoQuery || !store.isAuthenticated,\n fetchPolicy: \"cache-and-network\",\n pollInterval: 5 * 60 * 1000, // Poll every 5 minutes (was 1 min — too aggressive)\n },\n );\n\n // Active sessions query\n const {\n data: sessionsData,\n loading: sessionsLoading,\n refetch: refetchSessions,\n } = useQuery<ActiveSessionsQuery>(ActiveSessionsDocument, {\n skip: !store.isAuthenticated,\n fetchPolicy: \"cache-and-network\",\n });\n\n // Session mutations\n const [revokeSessionMutation] = useMutation<\n RevokeSessionMutation,\n RevokeSessionMutationVariables\n >(RevokeSessionDocument);\n\n const [revokeAllSessionsMutation] = useMutation<\n RevokeAllSessionsMutation,\n RevokeAllSessionsMutationVariables\n >(RevokeAllSessionsDocument);\n\n /**\n * Validate tokens\n */\n const validateTokens = async (\n input: ValidateTokensInput,\n ): Promise<TokenValidationResponse> => {\n try {\n const { data } = await validateTokensMutation({ variables: { input } });\n return data!.validateTokens;\n } catch (error) {\n console.error(\"Failed to validate tokens:\", error);\n throw error;\n }\n };\n\n /**\n * Check if token is expired\n */\n const isTokenExpired = (): boolean => {\n if (!data?.tokenInfo?.expiresAt) return true;\n return Date.now() / 1000 > data.tokenInfo.expiresAt;\n };\n\n /**\n * Get remaining time until token expires (in seconds)\n */\n const getTokenRemainingTime = (): number => {\n if (!data?.tokenInfo?.expiresAt) return 0;\n const remaining = data.tokenInfo.expiresAt - Date.now() / 1000;\n return Math.max(0, remaining);\n };\n\n /**\n * Revoke a specific session by ID\n */\n const revokeSession = async (sessionId: string): Promise<boolean> => {\n try {\n const { data } = await revokeSessionMutation({\n variables: { sessionId },\n });\n if (data?.revokeSession) {\n // Refetch active sessions to reflect the change\n await refetchSessions();\n }\n return data?.revokeSession ?? false;\n } catch (error) {\n console.error(\"Failed to revoke session:\", error);\n throw error;\n }\n };\n\n /**\n * Revoke all sessions except the current one\n */\n const revokeAllSessions = async (): Promise<{\n success: boolean;\n revokedCount: number;\n }> => {\n try {\n const { data } = await revokeAllSessionsMutation();\n if (data?.revokeAllSessions?.success) {\n // Refetch active sessions to reflect the change\n await refetchSessions();\n }\n return data?.revokeAllSessions ?? { success: false, revokedCount: 0 };\n } catch (error) {\n console.error(\"Failed to revoke all sessions:\", error);\n throw error;\n }\n };\n\n return {\n // Token operations\n validateTokens,\n isTokenExpired,\n getTokenRemainingTime,\n tokenInfo: data?.tokenInfo,\n // Session operations\n revokeSession,\n revokeAllSessions,\n activeSessions: sessionsData?.activeSessions ?? [],\n sessionsLoading,\n refetchSessions,\n // General state\n isLoading: loading || sessionsLoading,\n refetch,\n };\n}\n"],"mappings":";;;;AAyCA,SAAgB,EACd,IAAuC,EAAE,EACzC;CACA,IAAM,IAAQ,GAAc,EACtB,IAAwB,EAAQ,yBAAyB,IACzD,CAAC,KAA0B,EAG/B,EAAuB,EAEnB,EAAE,SAAM,YAAS,eAAY,EACjC,GACA;EACE,MAAM,KAAyB,CAAC,EAAM;EACtC,aAAa;EACb,cAAc,MAAS;EACxB,CACF,EAGK,EACJ,MAAM,GACN,SAAS,GACT,SAAS,MACP,EAA8B,GAAwB;EACxD,MAAM,CAAC,EAAM;EACb,aAAa;EACd,CAAC,EAGI,CAAC,KAAyB,EAG9B,EAAsB,EAElB,CAAC,KAA6B,EAGlC,EAA0B;AAyE5B,QAAO;EAEL,gBAtEqB,OACrB,MACqC;AACrC,OAAI;IACF,IAAM,EAAE,YAAS,MAAM,EAAuB,EAAE,WAAW,EAAE,UAAO,EAAE,CAAC;AACvE,WAAO,EAAM;YACN,GAAO;AAEd,UADA,QAAQ,MAAM,8BAA8B,EAAM,EAC5C;;;EA+DR,sBAvDK,GAAM,WAAW,YACf,KAAK,KAAK,GAAG,MAAO,EAAK,UAAU,YADF;EAwDxC,6BAjD0C;AAC1C,OAAI,CAAC,GAAM,WAAW,UAAW,QAAO;GACxC,IAAM,IAAY,EAAK,UAAU,YAAY,KAAK,KAAK,GAAG;AAC1D,UAAO,KAAK,IAAI,GAAG,EAAU;;EA+C7B,WAAW,GAAM;EAEjB,eA3CoB,OAAO,MAAwC;AACnE,OAAI;IACF,IAAM,EAAE,YAAS,MAAM,EAAsB,EAC3C,WAAW,EAAE,cAAW,EACzB,CAAC;AAKF,WAJI,GAAM,iBAER,MAAM,GAAiB,EAElB,GAAM,iBAAiB;YACvB,GAAO;AAEd,UADA,QAAQ,MAAM,6BAA6B,EAAM,EAC3C;;;EAgCR,mBAzBwB,YAGpB;AACJ,OAAI;IACF,IAAM,EAAE,YAAS,MAAM,GAA2B;AAKlD,WAJI,GAAM,mBAAmB,WAE3B,MAAM,GAAiB,EAElB,GAAM,qBAAqB;KAAE,SAAS;KAAO,cAAc;KAAG;YAC9D,GAAO;AAEd,UADA,QAAQ,MAAM,kCAAkC,EAAM,EAChD;;;EAaR,gBAAgB,GAAc,
|
|
1
|
+
{"version":3,"file":"useSessionOperations.js","names":[],"sources":["../../../src/auth/hooks/useSessionOperations.ts"],"sourcesContent":["import { useQuery, useMutation } from \"@apollo/client/react\";\nimport { useAuthStore } from \"../store/authStore\";\nimport {\n TokenInfoDocument,\n ValidateTokensDocument,\n ActiveSessionsDocument,\n RevokeSessionDocument,\n RevokeAllSessionsDocument,\n} from \"../../generated/global-operations\";\nimport type {\n ActiveSessionsQuery,\n RevokeSessionMutation,\n RevokeSessionMutationVariables,\n RevokeAllSessionsMutation,\n RevokeAllSessionsMutationVariables,\n} from \"../../generated/global-operations\";\nimport type { TokenValidationResponse, ValidateTokensInput } from \"../types\";\n\n// =============================================================================\n// GRAPHQL RESPONSE INTERFACES\n// =============================================================================\n\ninterface TokenInfoQueryResponse {\n tokenInfo: TokenValidationResponse;\n}\n\ninterface ValidateTokensMutationResponse {\n validateTokens: TokenValidationResponse;\n}\n\ninterface ValidateTokensMutationVariables {\n input: ValidateTokensInput;\n}\n\ninterface UseSessionOperationsOptions {\n disableTokenInfoQuery?: boolean;\n}\n\n/**\n * Hook for session/token management operations\n */\nexport function useSessionOperations(\n options: UseSessionOperationsOptions = {},\n) {\n const store = useAuthStore();\n const disableTokenInfoQuery = options.disableTokenInfoQuery ?? false;\n const [validateTokensMutation] = useMutation<\n ValidateTokensMutationResponse,\n ValidateTokensMutationVariables\n >(ValidateTokensDocument);\n\n const { data, loading, refetch } = useQuery<TokenInfoQueryResponse>(\n TokenInfoDocument,\n {\n skip: disableTokenInfoQuery || !store.isAuthenticated,\n fetchPolicy: \"cache-and-network\",\n pollInterval: 5 * 60 * 1000, // Poll every 5 minutes (was 1 min — too aggressive)\n },\n );\n\n // Active sessions query\n const {\n data: sessionsData,\n loading: sessionsLoading,\n refetch: refetchSessions,\n } = useQuery<ActiveSessionsQuery>(ActiveSessionsDocument, {\n skip: !store.isAuthenticated,\n fetchPolicy: \"cache-and-network\",\n });\n\n // Session mutations\n const [revokeSessionMutation] = useMutation<\n RevokeSessionMutation,\n RevokeSessionMutationVariables\n >(RevokeSessionDocument);\n\n const [revokeAllSessionsMutation] = useMutation<\n RevokeAllSessionsMutation,\n RevokeAllSessionsMutationVariables\n >(RevokeAllSessionsDocument);\n\n /**\n * Validate tokens\n */\n const validateTokens = async (\n input: ValidateTokensInput,\n ): Promise<TokenValidationResponse> => {\n try {\n const { data } = await validateTokensMutation({ variables: { input } });\n return data!.validateTokens;\n } catch (error) {\n console.error(\"Failed to validate tokens:\", error);\n throw error;\n }\n };\n\n /**\n * Check if token is expired\n */\n const isTokenExpired = (): boolean => {\n if (!data?.tokenInfo?.expiresAt) return true;\n return Date.now() / 1000 > data.tokenInfo.expiresAt;\n };\n\n /**\n * Get remaining time until token expires (in seconds)\n */\n const getTokenRemainingTime = (): number => {\n if (!data?.tokenInfo?.expiresAt) return 0;\n const remaining = data.tokenInfo.expiresAt - Date.now() / 1000;\n return Math.max(0, remaining);\n };\n\n /**\n * Revoke a specific session by ID\n */\n const revokeSession = async (sessionId: string): Promise<boolean> => {\n try {\n const { data } = await revokeSessionMutation({\n variables: { sessionId },\n });\n if (data?.revokeSession) {\n // Refetch active sessions to reflect the change\n await refetchSessions();\n }\n return data?.revokeSession ?? false;\n } catch (error) {\n console.error(\"Failed to revoke session:\", error);\n throw error;\n }\n };\n\n /**\n * Revoke all sessions except the current one\n */\n const revokeAllSessions = async (): Promise<{\n success: boolean;\n revokedCount: number;\n }> => {\n try {\n const { data } = await revokeAllSessionsMutation();\n if (data?.revokeAllSessions?.success) {\n // Refetch active sessions to reflect the change\n await refetchSessions();\n }\n return data?.revokeAllSessions ?? { success: false, revokedCount: 0 };\n } catch (error) {\n console.error(\"Failed to revoke all sessions:\", error);\n throw error;\n }\n };\n\n return {\n // Token operations\n validateTokens,\n isTokenExpired,\n getTokenRemainingTime,\n tokenInfo: data?.tokenInfo,\n // Session operations\n revokeSession,\n revokeAllSessions,\n activeSessions: sessionsData?.activeSessions?.sessions ?? [],\n sessionsLoading,\n refetchSessions,\n // General state\n isLoading: loading || sessionsLoading,\n refetch,\n };\n}\n"],"mappings":";;;;AAyCA,SAAgB,EACd,IAAuC,EAAE,EACzC;CACA,IAAM,IAAQ,GAAc,EACtB,IAAwB,EAAQ,yBAAyB,IACzD,CAAC,KAA0B,EAG/B,EAAuB,EAEnB,EAAE,SAAM,YAAS,eAAY,EACjC,GACA;EACE,MAAM,KAAyB,CAAC,EAAM;EACtC,aAAa;EACb,cAAc,MAAS;EACxB,CACF,EAGK,EACJ,MAAM,GACN,SAAS,GACT,SAAS,MACP,EAA8B,GAAwB;EACxD,MAAM,CAAC,EAAM;EACb,aAAa;EACd,CAAC,EAGI,CAAC,KAAyB,EAG9B,EAAsB,EAElB,CAAC,KAA6B,EAGlC,EAA0B;AAyE5B,QAAO;EAEL,gBAtEqB,OACrB,MACqC;AACrC,OAAI;IACF,IAAM,EAAE,YAAS,MAAM,EAAuB,EAAE,WAAW,EAAE,UAAO,EAAE,CAAC;AACvE,WAAO,EAAM;YACN,GAAO;AAEd,UADA,QAAQ,MAAM,8BAA8B,EAAM,EAC5C;;;EA+DR,sBAvDK,GAAM,WAAW,YACf,KAAK,KAAK,GAAG,MAAO,EAAK,UAAU,YADF;EAwDxC,6BAjD0C;AAC1C,OAAI,CAAC,GAAM,WAAW,UAAW,QAAO;GACxC,IAAM,IAAY,EAAK,UAAU,YAAY,KAAK,KAAK,GAAG;AAC1D,UAAO,KAAK,IAAI,GAAG,EAAU;;EA+C7B,WAAW,GAAM;EAEjB,eA3CoB,OAAO,MAAwC;AACnE,OAAI;IACF,IAAM,EAAE,YAAS,MAAM,EAAsB,EAC3C,WAAW,EAAE,cAAW,EACzB,CAAC;AAKF,WAJI,GAAM,iBAER,MAAM,GAAiB,EAElB,GAAM,iBAAiB;YACvB,GAAO;AAEd,UADA,QAAQ,MAAM,6BAA6B,EAAM,EAC3C;;;EAgCR,mBAzBwB,YAGpB;AACJ,OAAI;IACF,IAAM,EAAE,YAAS,MAAM,GAA2B;AAKlD,WAJI,GAAM,mBAAmB,WAE3B,MAAM,GAAiB,EAElB,GAAM,qBAAqB;KAAE,SAAS;KAAO,cAAc;KAAG;YAC9D,GAAO;AAEd,UADA,QAAQ,MAAM,kCAAkC,EAAM,EAChD;;;EAaR,gBAAgB,GAAc,gBAAgB,YAAY,EAAE;EAC5D;EACA;EAEA,WAAW,KAAW;EACtB;EACD"}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Security audit log page
|
|
2
|
+
* Security audit log page with pagination and export
|
|
3
|
+
*
|
|
4
|
+
* Fragment: AuditLogEntry_AuthAuditLog (graphql/operations/global/fragments/AuditLogEntry.fragment.graphql)
|
|
5
|
+
* Query: AuthAuditLogs (graphql/operations/global/queries/auditLog.graphql)
|
|
6
|
+
*
|
|
7
|
+
* TODO: When authAuditLogs backend migrates to AuthAuditLogConnection type,
|
|
8
|
+
* update this query to use: authAuditLogs { auditLogs: [...], totalCount, hasMore }
|
|
9
|
+
* and remove the client-side hasMore approximation below.
|
|
3
10
|
*/
|
|
4
11
|
export declare function AuditLogPage(): import("react/jsx-runtime").JSX.Element;
|
|
5
12
|
//# sourceMappingURL=AuditLogPage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuditLogPage.d.ts","sourceRoot":"","sources":["../../../src/auth/pages/AuditLogPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AuditLogPage.d.ts","sourceRoot":"","sources":["../../../src/auth/pages/AuditLogPage.tsx"],"names":[],"mappings":"AAaA;;;;;;;;;GASG;AACH,wBAAgB,YAAY,4CA2Q3B"}
|
|
@@ -1,56 +1,38 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import { useAuthAuditLogsQuery as e } from "../../generated/global-operations.js";
|
|
2
|
+
import { AlertIcon as t, SpinnerIcon as n } from "../components/AuthIcons.js";
|
|
3
|
+
import { formatDateTime as r } from "../utils/formatters.js";
|
|
4
|
+
import { useCreateAuthExportJobMutation as i } from "../../generated/wspace-operations.js";
|
|
5
|
+
import { useState as a } from "react";
|
|
6
|
+
import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
7
|
+
import { Fragment as s, jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
8
|
+
import { useFeatureFlagDetails as u } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
8
9
|
//#region src/auth/pages/AuditLogPage.tsx
|
|
9
|
-
var
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
action
|
|
14
|
-
description
|
|
15
|
-
ipAddress
|
|
16
|
-
userAgent
|
|
17
|
-
createdAt
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
`;
|
|
21
|
-
function l() {
|
|
22
|
-
let { t: s } = n(), l = (e, t) => {
|
|
23
|
-
let n = s(e);
|
|
10
|
+
var d = 20;
|
|
11
|
+
function f() {
|
|
12
|
+
let { t: f } = o(), p = (e, t) => {
|
|
13
|
+
let n = f(e);
|
|
24
14
|
return n === e ? t : n;
|
|
25
|
-
},
|
|
26
|
-
skip: !
|
|
15
|
+
}, [m, h] = a(1), g = (m - 1) * d, _ = u("auth.audit-log")?.value === !0, { data: v, loading: y, error: b } = e({
|
|
16
|
+
skip: !_,
|
|
27
17
|
fetchPolicy: "cache-and-network",
|
|
28
18
|
variables: { pagination: {
|
|
29
|
-
limit:
|
|
30
|
-
offset:
|
|
19
|
+
limit: d,
|
|
20
|
+
offset: g
|
|
31
21
|
} }
|
|
32
|
-
}),
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/* @__PURE__ */ r("p", {
|
|
47
|
-
className: "text-text-secondary",
|
|
48
|
-
children: l("auth.auditLog.notEnabled", "The Security Audit Log is not enabled for your account.")
|
|
49
|
-
})
|
|
50
|
-
]
|
|
51
|
-
})
|
|
52
|
-
});
|
|
53
|
-
let h = (e) => {
|
|
22
|
+
}), x = v?.authAuditLogs ?? [], S = x.length === d, [C, { loading: w }] = i(), [T, E] = a(null), D = async () => {
|
|
23
|
+
E(null);
|
|
24
|
+
try {
|
|
25
|
+
await C({ variables: { input: {
|
|
26
|
+
name: "Audit Log Export",
|
|
27
|
+
entityTypes: ["permission_audit_logs"],
|
|
28
|
+
format: "CSV",
|
|
29
|
+
exportScope: "INDIVIDUAL",
|
|
30
|
+
includeMetadata: !0
|
|
31
|
+
} } });
|
|
32
|
+
} catch (e) {
|
|
33
|
+
E(e instanceof Error ? e.message : "Failed to start export");
|
|
34
|
+
}
|
|
35
|
+
}, O = (e) => {
|
|
54
36
|
switch (e) {
|
|
55
37
|
case "LOGIN_SUCCESS": return "OK";
|
|
56
38
|
case "LOGIN_FAILURE": return "ER";
|
|
@@ -62,68 +44,171 @@ function l() {
|
|
|
62
44
|
default: return "AU";
|
|
63
45
|
}
|
|
64
46
|
};
|
|
65
|
-
return /* @__PURE__ */
|
|
47
|
+
return _ ? /* @__PURE__ */ l("div", {
|
|
66
48
|
className: "max-w-4xl mx-auto p-6",
|
|
67
|
-
children: [
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
className: "flex items-start gap-4",
|
|
94
|
-
children: [
|
|
95
|
-
/* @__PURE__ */ r("div", {
|
|
96
|
-
className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-bg-muted text-xs font-semibold text-text-secondary",
|
|
97
|
-
children: h(e.action)
|
|
98
|
-
}),
|
|
99
|
-
/* @__PURE__ */ i("div", {
|
|
100
|
-
className: "flex-1",
|
|
101
|
-
children: [
|
|
102
|
-
/* @__PURE__ */ r("h3", {
|
|
103
|
-
className: "text-lg font-medium text-text-primary mb-1",
|
|
104
|
-
children: e.action.replace(/_/g, " ")
|
|
105
|
-
}),
|
|
106
|
-
/* @__PURE__ */ r("p", {
|
|
107
|
-
className: "text-sm text-text-secondary mb-2",
|
|
108
|
-
children: t(e.createdAt)
|
|
109
|
-
}),
|
|
110
|
-
/* @__PURE__ */ i("p", {
|
|
111
|
-
className: "text-sm text-text-secondary",
|
|
112
|
-
children: [e.description ?? l("auth.auditLog.defaultDescription", "Authentication event"), e.ipAddress && ` • ${e.ipAddress}`]
|
|
113
|
-
})
|
|
114
|
-
]
|
|
115
|
-
}),
|
|
116
|
-
e.action.includes("FAILURE") && /* @__PURE__ */ r("span", {
|
|
117
|
-
className: "px-3 py-1 rounded-full bg-status-error-bg-subtle text-status-error-text text-sm font-medium",
|
|
118
|
-
children: l("auth.auditLog.failed", "Failed")
|
|
49
|
+
children: [
|
|
50
|
+
/* @__PURE__ */ l("div", {
|
|
51
|
+
className: "flex items-center justify-between mb-8",
|
|
52
|
+
children: [/* @__PURE__ */ c("h1", {
|
|
53
|
+
className: "text-3xl font-semibold text-text-primary",
|
|
54
|
+
children: p("auth.auditLog.title", "Security Audit Log")
|
|
55
|
+
}), /* @__PURE__ */ c("button", {
|
|
56
|
+
type: "button",
|
|
57
|
+
onClick: () => void D(),
|
|
58
|
+
disabled: w,
|
|
59
|
+
"aria-label": p("auth.auditLog.exportAriaLabel", "Export audit log"),
|
|
60
|
+
className: "flex items-center gap-2 px-4 py-2.5 rounded-xl border border-border-default text-text-primary bg-bg-surface hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium",
|
|
61
|
+
children: w ? /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ c(n, {
|
|
62
|
+
className: "w-4 h-4 animate-spin",
|
|
63
|
+
"aria-hidden": "true"
|
|
64
|
+
}), p("auth.auditLog.exporting", "Exporting...")] }) : /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ c("svg", {
|
|
65
|
+
className: "w-4 h-4",
|
|
66
|
+
fill: "none",
|
|
67
|
+
viewBox: "0 0 24 24",
|
|
68
|
+
stroke: "currentColor",
|
|
69
|
+
"aria-hidden": "true",
|
|
70
|
+
children: /* @__PURE__ */ c("path", {
|
|
71
|
+
strokeLinecap: "round",
|
|
72
|
+
strokeLinejoin: "round",
|
|
73
|
+
strokeWidth: 2,
|
|
74
|
+
d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
|
|
119
75
|
})
|
|
76
|
+
}), p("auth.auditLog.export", "Export")] })
|
|
77
|
+
})]
|
|
78
|
+
}),
|
|
79
|
+
T && /* @__PURE__ */ l("div", {
|
|
80
|
+
role: "alert",
|
|
81
|
+
className: "mb-6 flex items-start gap-3 p-4 rounded-xl bg-status-error-bg-subtle border border-status-error-border",
|
|
82
|
+
children: [/* @__PURE__ */ c(t, {
|
|
83
|
+
className: "w-5 h-5 text-status-error-text flex-shrink-0 mt-0.5",
|
|
84
|
+
"aria-hidden": "true"
|
|
85
|
+
}), /* @__PURE__ */ c("p", {
|
|
86
|
+
className: "text-sm text-status-error-text",
|
|
87
|
+
children: T
|
|
88
|
+
})]
|
|
89
|
+
}),
|
|
90
|
+
y ? /* @__PURE__ */ l("div", {
|
|
91
|
+
"aria-live": "polite",
|
|
92
|
+
"aria-busy": "true",
|
|
93
|
+
className: "text-center py-12",
|
|
94
|
+
children: [/* @__PURE__ */ c(n, {
|
|
95
|
+
className: "w-8 h-8 mx-auto text-text-link animate-spin",
|
|
96
|
+
"aria-hidden": "true"
|
|
97
|
+
}), /* @__PURE__ */ c("p", {
|
|
98
|
+
className: "mt-3 text-text-secondary",
|
|
99
|
+
children: p("auth.auditLog.loading", "Loading audit events...")
|
|
100
|
+
})]
|
|
101
|
+
}) : b ? /* @__PURE__ */ c("div", {
|
|
102
|
+
role: "alert",
|
|
103
|
+
className: "text-center py-12 border border-status-error-border rounded-xl bg-status-error-bg-subtle",
|
|
104
|
+
children: /* @__PURE__ */ c("p", {
|
|
105
|
+
className: "text-status-error-text",
|
|
106
|
+
children: p("auth.auditLog.error", "Unable to load audit events")
|
|
107
|
+
})
|
|
108
|
+
}) : x.length === 0 ? /* @__PURE__ */ c("div", {
|
|
109
|
+
className: "text-center py-12",
|
|
110
|
+
role: "status",
|
|
111
|
+
children: /* @__PURE__ */ c("p", {
|
|
112
|
+
className: "text-text-secondary",
|
|
113
|
+
children: p("auth.auditLog.noEvents", "No security events recorded")
|
|
114
|
+
})
|
|
115
|
+
}) : /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ c("div", {
|
|
116
|
+
className: "space-y-4",
|
|
117
|
+
"aria-live": "polite",
|
|
118
|
+
"aria-busy": y,
|
|
119
|
+
children: x.map((e) => /* @__PURE__ */ c("div", {
|
|
120
|
+
className: "p-6 rounded-xl border border-border-subtle bg-bg-surface shadow-sm",
|
|
121
|
+
children: /* @__PURE__ */ l("div", {
|
|
122
|
+
className: "flex items-start gap-4",
|
|
123
|
+
children: [
|
|
124
|
+
/* @__PURE__ */ c("div", {
|
|
125
|
+
className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-bg-muted text-xs font-semibold text-text-secondary",
|
|
126
|
+
"aria-hidden": "true",
|
|
127
|
+
children: O(e.action)
|
|
128
|
+
}),
|
|
129
|
+
/* @__PURE__ */ l("div", {
|
|
130
|
+
className: "flex-1",
|
|
131
|
+
children: [
|
|
132
|
+
/* @__PURE__ */ c("h3", {
|
|
133
|
+
className: "text-lg font-medium text-text-primary mb-1",
|
|
134
|
+
children: e.action.replace(/_/g, " ")
|
|
135
|
+
}),
|
|
136
|
+
/* @__PURE__ */ c("p", {
|
|
137
|
+
className: "text-sm text-text-secondary mb-2",
|
|
138
|
+
children: /* @__PURE__ */ c("time", {
|
|
139
|
+
dateTime: e.createdAt,
|
|
140
|
+
children: r(e.createdAt)
|
|
141
|
+
})
|
|
142
|
+
}),
|
|
143
|
+
/* @__PURE__ */ l("p", {
|
|
144
|
+
className: "text-sm text-text-secondary",
|
|
145
|
+
children: [e.description ?? p("auth.auditLog.defaultDescription", "Authentication event"), e.ipAddress && ` • ${e.ipAddress}`]
|
|
146
|
+
})
|
|
147
|
+
]
|
|
148
|
+
}),
|
|
149
|
+
e.action.includes("FAILURE") && /* @__PURE__ */ c("span", {
|
|
150
|
+
role: "status",
|
|
151
|
+
className: "px-3 py-1 rounded-full bg-status-error-bg-subtle text-status-error-text text-sm font-medium",
|
|
152
|
+
children: p("auth.auditLog.failed", "Failed")
|
|
153
|
+
})
|
|
154
|
+
]
|
|
155
|
+
})
|
|
156
|
+
}, e.id))
|
|
157
|
+
}), /* @__PURE__ */ l("nav", {
|
|
158
|
+
"aria-label": p("auth.auditLog.paginationAriaLabel", "Audit log pagination"),
|
|
159
|
+
className: "mt-6 flex items-center justify-between",
|
|
160
|
+
children: [/* @__PURE__ */ l("p", {
|
|
161
|
+
className: "text-sm text-text-secondary",
|
|
162
|
+
children: [
|
|
163
|
+
p("auth.auditLog.page", "Page"),
|
|
164
|
+
" ",
|
|
165
|
+
m
|
|
120
166
|
]
|
|
167
|
+
}), /* @__PURE__ */ l("div", {
|
|
168
|
+
className: "flex gap-2",
|
|
169
|
+
children: [/* @__PURE__ */ c("button", {
|
|
170
|
+
type: "button",
|
|
171
|
+
onClick: () => h((e) => Math.max(1, e - 1)),
|
|
172
|
+
disabled: m === 1,
|
|
173
|
+
"aria-label": p("auth.auditLog.previousPageAriaLabel", "Go to previous page"),
|
|
174
|
+
className: "px-4 py-2 rounded-lg border border-border-default text-text-primary bg-bg-surface disabled:opacity-50 disabled:cursor-not-allowed hover:bg-bg-sunken transition-colors",
|
|
175
|
+
children: p("auth.auditLog.previous", "Previous")
|
|
176
|
+
}), /* @__PURE__ */ c("button", {
|
|
177
|
+
type: "button",
|
|
178
|
+
onClick: () => h((e) => e + 1),
|
|
179
|
+
disabled: !S,
|
|
180
|
+
"aria-label": p("auth.auditLog.nextPageAriaLabel", "Go to next page"),
|
|
181
|
+
className: "px-4 py-2 rounded-lg border border-border-default text-text-primary bg-bg-surface disabled:opacity-50 disabled:cursor-not-allowed hover:bg-bg-sunken transition-colors",
|
|
182
|
+
children: p("auth.auditLog.next", "Next")
|
|
183
|
+
})]
|
|
184
|
+
})]
|
|
185
|
+
})] })
|
|
186
|
+
]
|
|
187
|
+
}) : /* @__PURE__ */ c("div", {
|
|
188
|
+
className: "max-w-4xl mx-auto p-6",
|
|
189
|
+
children: /* @__PURE__ */ l("div", {
|
|
190
|
+
className: "text-center py-16 border-2 border-dashed border-border-subtle rounded-xl",
|
|
191
|
+
children: [
|
|
192
|
+
/* @__PURE__ */ c("div", {
|
|
193
|
+
className: "w-16 h-16 mx-auto mb-4 rounded-full bg-status-warning-bg-subtle flex items-center justify-center",
|
|
194
|
+
children: /* @__PURE__ */ c(t, {
|
|
195
|
+
className: "w-8 h-8 text-status-warning-text",
|
|
196
|
+
"aria-hidden": "true"
|
|
197
|
+
})
|
|
198
|
+
}),
|
|
199
|
+
/* @__PURE__ */ c("h2", {
|
|
200
|
+
className: "text-xl font-semibold text-text-primary mb-2",
|
|
201
|
+
children: p("auth.auditLog.notAvailable", "Page Not Available")
|
|
202
|
+
}),
|
|
203
|
+
/* @__PURE__ */ c("p", {
|
|
204
|
+
className: "text-text-secondary",
|
|
205
|
+
children: p("auth.auditLog.notEnabled", "The Security Audit Log is not enabled for your account.")
|
|
121
206
|
})
|
|
122
|
-
|
|
123
|
-
})
|
|
207
|
+
]
|
|
208
|
+
})
|
|
124
209
|
});
|
|
125
210
|
}
|
|
126
211
|
//#endregion
|
|
127
|
-
export {
|
|
212
|
+
export { f as AuditLogPage };
|
|
128
213
|
|
|
129
214
|
//# sourceMappingURL=AuditLogPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuditLogPage.js","names":[],"sources":["../../../src/auth/pages/AuditLogPage.tsx"],"sourcesContent":["import { gql } from \"@apollo/client\";\nimport { useQuery } from \"@apollo/client/react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { useFeatureFlagDetails } from \"@burdenoff/fe-libs/shared/feature-flags\";\nimport { formatDateTime } from \"../utils\";\nimport { AlertIcon } from \"../components/AuthIcons\";\n\ninterface AuthAuditLogEntry {\n id: string;\n action: string;\n description?: string | null;\n ipAddress?: string | null;\n userAgent?: string | null;\n createdAt: string;\n}\n\ninterface AuthAuditLogsQueryResult {\n authAuditLogs: AuthAuditLogEntry[];\n}\n\nconst AUTH_AUDIT_LOGS_QUERY = gql`\n query AuthAuditLogs($pagination: PaginationInput) {\n authAuditLogs(pagination: $pagination) {\n id\n action\n description\n ipAddress\n userAgent\n createdAt\n }\n }\n`;\n\n/**\n * Security audit log page\n */\nexport function AuditLogPage() {\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 // Default-deny: only show when the flag is explicitly enabled in the system.\n // useFeatureFlag uses default-allow (returns true when not found), so we use\n // useFeatureFlagDetails + explicit value check instead.\n const auditLogFlag = useFeatureFlagDetails(\"auth.audit-log\");\n const showAuditLog = auditLogFlag?.value === true;\n const { data, loading, error } = useQuery<AuthAuditLogsQueryResult>(\n AUTH_AUDIT_LOGS_QUERY,\n {\n skip: !showAuditLog,\n fetchPolicy: \"cache-and-network\",\n variables: { pagination: { limit: 50, offset: 0 } },\n },\n );\n const auditLogs = data?.authAuditLogs ?? [];\n\n if (!showAuditLog) {\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <div className=\"text-center py-16 border-2 border-dashed border-border-subtle rounded-xl\">\n <div className=\"w-16 h-16 mx-auto mb-4 rounded-full bg-status-warning-bg-subtle flex items-center justify-center\">\n <AlertIcon className=\"w-8 h-8 text-status-warning-text\" />\n </div>\n <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr(\"auth.auditLog.notAvailable\", \"Page Not Available\")}\n </h2>\n <p className=\"text-text-secondary\">\n {tr(\n \"auth.auditLog.notEnabled\",\n \"The Security Audit Log is not enabled for your account.\",\n )}\n </p>\n </div>\n </div>\n );\n }\n\n const getEventMarker = (eventType: string) => {\n switch (eventType) {\n case \"LOGIN_SUCCESS\":\n return \"OK\";\n case \"LOGIN_FAILURE\":\n return \"ER\";\n case \"LOGOUT\":\n return \"LO\";\n case \"PASSWORD_CHANGE\":\n return \"PW\";\n case \"MFA_ENABLED\":\n case \"MFA_VERIFIED\":\n return \"MF\";\n case \"ANOMALY_DETECTED\":\n return \"AL\";\n default:\n return \"AU\";\n }\n };\n\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <h1 className=\"text-3xl font-semibold text-text-primary mb-8\">\n {tr(\"auth.auditLog.title\", \"Security Audit Log\")}\n </h1>\n\n {loading ? (\n <div className=\"text-center py-12\">\n <p className=\"text-text-secondary\">\n {tr(\"auth.auditLog.loading\", \"Loading audit events...\")}\n </p>\n </div>\n ) : error ? (\n <div className=\"text-center py-12 border border-status-error-border rounded-xl bg-status-error-bg-subtle\">\n <p className=\"text-status-error-text\">\n {tr(\"auth.auditLog.error\", \"Unable to load audit events\")}\n </p>\n </div>\n ) : auditLogs.length === 0 ? (\n <div className=\"text-center py-12\">\n <p className=\"text-text-secondary\">\n {tr(\"auth.auditLog.noEvents\", \"No security events recorded\")}\n </p>\n </div>\n ) : (\n <div className=\"space-y-4\">\n {auditLogs.map((event) => (\n <div\n key={event.id}\n className=\"p-6 rounded-xl border border-border-subtle bg-bg-surface shadow-sm\"\n >\n <div className=\"flex items-start gap-4\">\n <div className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-bg-muted text-xs font-semibold text-text-secondary\">\n {getEventMarker(event.action)}\n </div>\n <div className=\"flex-1\">\n <h3 className=\"text-lg font-medium text-text-primary mb-1\">\n {event.action.replace(/_/g, \" \")}\n </h3>\n <p className=\"text-sm text-text-secondary mb-2\">\n {formatDateTime(event.createdAt)}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {event.description ??\n tr(\n \"auth.auditLog.defaultDescription\",\n \"Authentication event\",\n )}\n {event.ipAddress && ` • ${event.ipAddress}`}\n </p>\n </div>\n {event.action.includes(\"FAILURE\") && (\n <span className=\"px-3 py-1 rounded-full bg-status-error-bg-subtle text-status-error-text text-sm font-medium\">\n {tr(\"auth.auditLog.failed\", \"Failed\")}\n </span>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAoBA,IAAM,IAAwB,CAAG;;;;;;;;;;;;AAgBjC,SAAgB,IAAe;CAC7B,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAOnC,IADe,EAAsB,iBAAiB,EACzB,UAAU,IACvC,EAAE,SAAM,YAAS,aAAU,EAC/B,GACA;EACE,MAAM,CAAC;EACP,aAAa;EACb,WAAW,EAAE,YAAY;GAAE,OAAO;GAAI,QAAQ;GAAG,EAAE;EACpD,CACF,EACK,IAAY,GAAM,iBAAiB,EAAE;AAE3C,KAAI,CAAC,EACH,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD,EAAW,WAAU,oCAAqC,CAAA;KACtD,CAAA;IACN,kBAAC,MAAD;KAAI,WAAU;eACX,EAAG,8BAA8B,qBAAqB;KACpD,CAAA;IACL,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,4BACA,0DACD;KACC,CAAA;IACA;;EACF,CAAA;CAIV,IAAM,KAAkB,MAAsB;AAC5C,UAAQ,GAAR;GACE,KAAK,gBACH,QAAO;GACT,KAAK,gBACH,QAAO;GACT,KAAK,SACH,QAAO;GACT,KAAK,kBACH,QAAO;GACT,KAAK;GACL,KAAK,eACH,QAAO;GACT,KAAK,mBACH,QAAO;GACT,QACE,QAAO;;;AAIb,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,MAAD;GAAI,WAAU;aACX,EAAG,uBAAuB,qBAAqB;GAC7C,CAAA,EAEJ,IACC,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,KAAD;IAAG,WAAU;cACV,EAAG,yBAAyB,0BAA0B;IACrD,CAAA;GACA,CAAA,GACJ,IACF,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,KAAD;IAAG,WAAU;cACV,EAAG,uBAAuB,8BAA8B;IACvD,CAAA;GACA,CAAA,GACJ,EAAU,WAAW,IACvB,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,KAAD;IAAG,WAAU;cACV,EAAG,0BAA0B,8BAA8B;IAC1D,CAAA;GACA,CAAA,GAEN,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAU,KAAK,MACd,kBAAC,OAAD;IAEE,WAAU;cAEV,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAe,EAAM,OAAO;OACzB,CAAA;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAM,OAAO,QAAQ,MAAM,IAAI;SAC7B,CAAA;QACL,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAe,EAAM,UAAU;SAC9B,CAAA;QACJ,kBAAC,KAAD;SAAG,WAAU;mBAAb,CACG,EAAM,eACL,EACE,oCACA,uBACD,EACF,EAAM,aAAa,MAAM,EAAM,YAC9B;;QACA;;MACL,EAAM,OAAO,SAAS,UAAU,IAC/B,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAG,wBAAwB,SAAS;OAChC,CAAA;MAEL;;IACF,EA7BC,EAAM,GA6BP,CACN;GACE,CAAA,CAEJ"}
|
|
1
|
+
{"version":3,"file":"AuditLogPage.js","names":[],"sources":["../../../src/auth/pages/AuditLogPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { useFeatureFlagDetails } from \"@burdenoff/fe-libs/shared/feature-flags\";\nimport { formatDateTime } from \"../utils\";\nimport { AlertIcon, SpinnerIcon } from \"../components/AuthIcons\";\nimport {\n useAuthAuditLogsQuery,\n type AuditLogEntry_AuthAuditLogFragment,\n} from \"../../generated/global-operations\";\nimport { useCreateAuthExportJobMutation } from \"../../generated/wspace-operations\";\n\nconst PAGE_SIZE = 20;\n\n/**\n * Security audit log page with pagination and export\n *\n * Fragment: AuditLogEntry_AuthAuditLog (graphql/operations/global/fragments/AuditLogEntry.fragment.graphql)\n * Query: AuthAuditLogs (graphql/operations/global/queries/auditLog.graphql)\n *\n * TODO: When authAuditLogs backend migrates to AuthAuditLogConnection type,\n * update this query to use: authAuditLogs { auditLogs: [...], totalCount, hasMore }\n * and remove the client-side hasMore approximation below.\n */\nexport function AuditLogPage() {\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 [page, setPage] = useState(1);\n const offset = (page - 1) * PAGE_SIZE;\n\n // Default-deny: only show when the flag is explicitly enabled in the system.\n // useFeatureFlag uses default-allow (returns true when not found), so we use\n // useFeatureFlagDetails + explicit value check instead.\n const auditLogFlag = useFeatureFlagDetails(\"auth.audit-log\");\n const showAuditLog = auditLogFlag?.value === true;\n\n const { data, loading, error } = useAuthAuditLogsQuery({\n skip: !showAuditLog,\n fetchPolicy: \"cache-and-network\",\n variables: { pagination: { limit: PAGE_SIZE, offset } },\n });\n\n const auditLogs = data?.authAuditLogs ?? [];\n // Approximate hasMore: if we received a full page there may be more\n const hasMore = auditLogs.length === PAGE_SIZE;\n\n const [createExportJob, { loading: isExporting }] =\n useCreateAuthExportJobMutation();\n const [exportError, setExportError] = useState<string | null>(null);\n\n const handleExport = async () => {\n setExportError(null);\n try {\n await createExportJob({\n variables: {\n input: {\n name: \"Audit Log Export\",\n // permission_audit_logs covers auth audit logs in the export system\n entityTypes: [\"permission_audit_logs\"],\n format: \"CSV\",\n exportScope: \"INDIVIDUAL\",\n includeMetadata: true,\n },\n },\n });\n } catch (err) {\n setExportError(\n err instanceof Error ? err.message : \"Failed to start export\",\n );\n }\n };\n\n const getEventMarker = (eventType: string) => {\n switch (eventType) {\n case \"LOGIN_SUCCESS\":\n return \"OK\";\n case \"LOGIN_FAILURE\":\n return \"ER\";\n case \"LOGOUT\":\n return \"LO\";\n case \"PASSWORD_CHANGE\":\n return \"PW\";\n case \"MFA_ENABLED\":\n case \"MFA_VERIFIED\":\n return \"MF\";\n case \"ANOMALY_DETECTED\":\n return \"AL\";\n default:\n return \"AU\";\n }\n };\n\n if (!showAuditLog) {\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n <div className=\"text-center py-16 border-2 border-dashed border-border-subtle rounded-xl\">\n <div className=\"w-16 h-16 mx-auto mb-4 rounded-full bg-status-warning-bg-subtle flex items-center justify-center\">\n <AlertIcon\n className=\"w-8 h-8 text-status-warning-text\"\n aria-hidden=\"true\"\n />\n </div>\n <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr(\"auth.auditLog.notAvailable\", \"Page Not Available\")}\n </h2>\n <p className=\"text-text-secondary\">\n {tr(\n \"auth.auditLog.notEnabled\",\n \"The Security Audit Log is not enabled for your account.\",\n )}\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"max-w-4xl mx-auto p-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-8\">\n <h1 className=\"text-3xl font-semibold text-text-primary\">\n {tr(\"auth.auditLog.title\", \"Security Audit Log\")}\n </h1>\n <button\n type=\"button\"\n onClick={() => void handleExport()}\n disabled={isExporting}\n aria-label={tr(\"auth.auditLog.exportAriaLabel\", \"Export audit log\")}\n className=\"flex items-center gap-2 px-4 py-2.5 rounded-xl border border-border-default text-text-primary bg-bg-surface hover:bg-bg-sunken disabled:opacity-50 disabled:cursor-not-allowed transition-colors font-medium\"\n >\n {isExporting ? (\n <>\n <SpinnerIcon\n className=\"w-4 h-4 animate-spin\"\n aria-hidden=\"true\"\n />\n {tr(\"auth.auditLog.exporting\", \"Exporting...\")}\n </>\n ) : (\n <>\n <svg\n className=\"w-4 h-4\"\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=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"\n />\n </svg>\n {tr(\"auth.auditLog.export\", \"Export\")}\n </>\n )}\n </button>\n </div>\n\n {/* Export error */}\n {exportError && (\n <div\n role=\"alert\"\n className=\"mb-6 flex items-start gap-3 p-4 rounded-xl bg-status-error-bg-subtle border border-status-error-border\"\n >\n <AlertIcon\n className=\"w-5 h-5 text-status-error-text flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n <p className=\"text-sm text-status-error-text\">{exportError}</p>\n </div>\n )}\n\n {loading ? (\n <div aria-live=\"polite\" aria-busy=\"true\" className=\"text-center py-12\">\n <SpinnerIcon\n className=\"w-8 h-8 mx-auto text-text-link animate-spin\"\n aria-hidden=\"true\"\n />\n <p className=\"mt-3 text-text-secondary\">\n {tr(\"auth.auditLog.loading\", \"Loading audit events...\")}\n </p>\n </div>\n ) : error ? (\n <div\n role=\"alert\"\n className=\"text-center py-12 border border-status-error-border rounded-xl bg-status-error-bg-subtle\"\n >\n <p className=\"text-status-error-text\">\n {tr(\"auth.auditLog.error\", \"Unable to load audit events\")}\n </p>\n </div>\n ) : auditLogs.length === 0 ? (\n <div className=\"text-center py-12\" role=\"status\">\n <p className=\"text-text-secondary\">\n {tr(\"auth.auditLog.noEvents\", \"No security events recorded\")}\n </p>\n </div>\n ) : (\n <>\n <div className=\"space-y-4\" aria-live=\"polite\" aria-busy={loading}>\n {auditLogs.map((event: AuditLogEntry_AuthAuditLogFragment) => (\n <div\n key={event.id}\n className=\"p-6 rounded-xl border border-border-subtle bg-bg-surface shadow-sm\"\n >\n <div className=\"flex items-start gap-4\">\n <div\n className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-bg-muted text-xs font-semibold text-text-secondary\"\n aria-hidden=\"true\"\n >\n {getEventMarker(event.action)}\n </div>\n <div className=\"flex-1\">\n <h3 className=\"text-lg font-medium text-text-primary mb-1\">\n {event.action.replace(/_/g, \" \")}\n </h3>\n <p className=\"text-sm text-text-secondary mb-2\">\n <time dateTime={event.createdAt}>\n {formatDateTime(event.createdAt)}\n </time>\n </p>\n <p className=\"text-sm text-text-secondary\">\n {event.description ??\n tr(\n \"auth.auditLog.defaultDescription\",\n \"Authentication event\",\n )}\n {event.ipAddress && ` • ${event.ipAddress}`}\n </p>\n </div>\n {event.action.includes(\"FAILURE\") && (\n <span\n role=\"status\"\n className=\"px-3 py-1 rounded-full bg-status-error-bg-subtle text-status-error-text text-sm font-medium\"\n >\n {tr(\"auth.auditLog.failed\", \"Failed\")}\n </span>\n )}\n </div>\n </div>\n ))}\n </div>\n\n {/* Pagination */}\n <nav\n aria-label={tr(\n \"auth.auditLog.paginationAriaLabel\",\n \"Audit log pagination\",\n )}\n className=\"mt-6 flex items-center justify-between\"\n >\n <p className=\"text-sm text-text-secondary\">\n {tr(\"auth.auditLog.page\", \"Page\")} {page}\n </p>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => setPage((prev) => Math.max(1, prev - 1))}\n disabled={page === 1}\n aria-label={tr(\n \"auth.auditLog.previousPageAriaLabel\",\n \"Go to previous page\",\n )}\n className=\"px-4 py-2 rounded-lg border border-border-default text-text-primary bg-bg-surface disabled:opacity-50 disabled:cursor-not-allowed hover:bg-bg-sunken transition-colors\"\n >\n {tr(\"auth.auditLog.previous\", \"Previous\")}\n </button>\n <button\n type=\"button\"\n onClick={() => setPage((prev) => prev + 1)}\n disabled={!hasMore}\n aria-label={tr(\n \"auth.auditLog.nextPageAriaLabel\",\n \"Go to next page\",\n )}\n className=\"px-4 py-2 rounded-lg border border-border-default text-text-primary bg-bg-surface disabled:opacity-50 disabled:cursor-not-allowed hover:bg-bg-sunken transition-colors\"\n >\n {tr(\"auth.auditLog.next\", \"Next\")}\n </button>\n </div>\n </nav>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MfaSetupPage.d.ts","sourceRoot":"","sources":["../../../src/auth/pages/MfaSetupPage.tsx"],"names":[],"mappings":"AAcA;;GAEG;AACH,wBAAgB,YAAY,
|
|
1
|
+
{"version":3,"file":"MfaSetupPage.d.ts","sourceRoot":"","sources":["../../../src/auth/pages/MfaSetupPage.tsx"],"names":[],"mappings":"AAcA;;GAEG;AACH,wBAAgB,YAAY,4CAqgB3B"}
|
|
@@ -26,14 +26,14 @@ function m() {
|
|
|
26
26
|
/* @__PURE__ */ f(a, {
|
|
27
27
|
open: M,
|
|
28
28
|
onOpenChange: N,
|
|
29
|
-
title: "Disable two-factor authentication?",
|
|
30
|
-
description: "Your account will become less secure and future sign-ins will only require your password until MFA is re-enabled.",
|
|
31
|
-
confirmText: "Disable MFA",
|
|
29
|
+
title: h("auth.mfaSetup.disableTitle", "Disable two-factor authentication?"),
|
|
30
|
+
description: h("auth.mfaSetup.disableDesc", "Your account will become less secure and future sign-ins will only require your password until MFA is re-enabled."),
|
|
31
|
+
confirmText: h("auth.mfaSetup.disableConfirm", "Disable MFA"),
|
|
32
32
|
variant: "warning",
|
|
33
33
|
isPending: y,
|
|
34
34
|
onConfirm: async () => {
|
|
35
35
|
try {
|
|
36
|
-
D(null), k(null), await v(), k("Two-factor authentication has been disabled."), N(!1);
|
|
36
|
+
D(null), k(null), await v(), k(h("auth.mfaSetup.disableSuccess", "Two-factor authentication has been disabled.")), N(!1);
|
|
37
37
|
} catch (e) {
|
|
38
38
|
console.error("Failed to disable MFA:", e), D(e instanceof Error ? e.message : "Failed to disable MFA. Please try again.");
|
|
39
39
|
}
|
|
@@ -203,7 +203,7 @@ function m() {
|
|
|
203
203
|
return;
|
|
204
204
|
}
|
|
205
205
|
try {
|
|
206
|
-
D(null), await _(w) && (C(null), T(""), k("Two-factor authentication has been enabled successfully!"));
|
|
206
|
+
D(null), await _(w) && (C(null), T(""), k(h("auth.mfaSetup.enableSuccess", "Two-factor authentication has been enabled successfully!")));
|
|
207
207
|
} catch (e) {
|
|
208
208
|
console.error("Failed to verify MFA code:", e), D(e instanceof Error ? e.message : "Invalid verification code. Please try again.");
|
|
209
209
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MfaSetupPage.js","names":[],"sources":["../../../src/auth/pages/MfaSetupPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { QRCodeSVG } from \"qrcode.react\";\nimport { useMfaOperations } from \"../hooks\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { ConfirmActionDialog } from \"../components/ConfirmActionDialog\";\nimport { SpinnerIcon, AlertIcon } from \"../components/AuthIcons\";\nimport type { MFAMethod, MFASetupResult } from \"../types\";\nimport {\n nativeCopyText,\n nativeImpact,\n nativeNotify,\n} from \"../../utils/nativeBridge\";\n\n/**\n * MFA setup and management page with professional UI\n */\nexport function MfaSetupPage() {\n const { 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 { setupMFA, enableMFA, disableMFA, isLoading } = useMfaOperations();\n const user = useAuthStore((state) => state.user);\n const isAuthenticated = useAuthStore((state) => state.isAuthenticated);\n const [setupData, setSetupData] = useState<MFASetupResult | null>(null);\n const [verificationCode, setVerificationCode] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [backupCodesCopied, setBackupCodesCopied] = useState(false);\n const [showDisableDialog, setShowDisableDialog] = useState(false);\n\n const handleSetupMfa = async (method: MFAMethod) => {\n try {\n setError(null);\n setSuccessMessage(null);\n const data = await setupMFA(method);\n setSetupData(data);\n } catch (err) {\n console.error(\"Failed to setup MFA:\", err);\n setError(\n err instanceof Error\n ? err.message\n : \"Failed to setup MFA. Please try again.\",\n );\n }\n };\n\n const handleVerifyAndEnable = async () => {\n if (verificationCode.length !== 6) {\n setError(tr(\"auth.mfaSetup.enter6Digit\", \"Please enter a 6-digit code\"));\n return;\n }\n\n try {\n setError(null);\n const success = await enableMFA(verificationCode);\n if (success) {\n setSetupData(null);\n setVerificationCode(\"\");\n setSuccessMessage(\n \"Two-factor authentication has been enabled successfully!\",\n );\n }\n } catch (err) {\n console.error(\"Failed to verify MFA code:\", err);\n setError(\n err instanceof Error\n ? err.message\n : \"Invalid verification code. Please try again.\",\n );\n }\n };\n\n const handleDisableMfa = async () => {\n try {\n setError(null);\n setSuccessMessage(null);\n await disableMFA();\n setSuccessMessage(\"Two-factor authentication has been disabled.\");\n setShowDisableDialog(false);\n } catch (err) {\n console.error(\"Failed to disable MFA:\", err);\n setError(\n err instanceof Error\n ? err.message\n : \"Failed to disable MFA. Please try again.\",\n );\n }\n };\n\n const copyBackupCodes = async () => {\n if (setupData?.backupCodes) {\n void nativeImpact(\"light\");\n const ok = await nativeCopyText(setupData.backupCodes.join(\"\\n\"));\n if (ok) {\n setBackupCodesCopied(true);\n void nativeNotify(\"success\");\n setTimeout(() => setBackupCodesCopied(false), 2000);\n } else {\n void nativeNotify(\"error\");\n }\n }\n };\n\n const cancelSetup = () => {\n setSetupData(null);\n setVerificationCode(\"\");\n setError(null);\n };\n\n // Authentication check\n if (!isAuthenticated) {\n return (\n <div className=\"max-w-2xl mx-auto p-6\">\n <div className=\"text-center py-16 border-2 border-dashed border-border-default rounded-xl\">\n <div className=\"w-16 h-16 mx-auto mb-4 rounded-full bg-status-warning-bg-subtle flex items-center justify-center\">\n <AlertIcon className=\"w-8 h-8 text-status-warning-text\" />\n </div>\n <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr(\"auth.mfaSetup.authRequired\", \"Authentication Required\")}\n </h2>\n <p className=\"text-text-secondary\">\n {tr(\n \"auth.mfaSetup.signInToManage\",\n \"Please sign in to manage MFA settings\",\n )}\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"max-w-2xl mx-auto p-6\">\n <ConfirmActionDialog\n open={showDisableDialog}\n onOpenChange={setShowDisableDialog}\n title=\"Disable two-factor authentication?\"\n description=\"Your account will become less secure and future sign-ins will only require your password until MFA is re-enabled.\"\n confirmText=\"Disable MFA\"\n variant=\"warning\"\n isPending={isLoading}\n onConfirm={handleDisableMfa}\n />\n {/* Header */}\n <div className=\"mb-8\">\n <h1 className=\"text-3xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\"auth.mfaSetup.title\", \"Two-Factor Authentication\")}\n </h1>\n <p className=\"text-[var(--color-text-secondary)] mt-2\">\n {tr(\n \"auth.mfaSetup.subtitle\",\n \"Add an extra layer of security to your account\",\n )}\n </p>\n </div>\n\n {/* Success Message */}\n {successMessage && (\n <div className=\"mb-6 p-4 rounded-xl bg-[var(--color-status-success-bg-subtle)] border border-[var(--color-status-success-border)]\">\n <div className=\"flex items-center gap-3\">\n <svg\n className=\"w-5 h-5 text-[var(--color-status-success-text)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <p className=\"text-[var(--color-status-success-text)]\">\n {successMessage}\n </p>\n </div>\n </div>\n )}\n\n {/* Error Message */}\n {error && (\n <div className=\"mb-6 flex items-start gap-3 p-4 rounded-xl bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)]\">\n <AlertIcon className=\"w-5 h-5 text-[var(--color-status-error-text)] flex-shrink-0 mt-0.5\" />\n <div className=\"flex-1\">\n <p className=\"text-sm text-[var(--color-status-error-text)]\">\n {error}\n </p>\n <button\n onClick={() => setError(null)}\n className=\"mt-2 text-sm text-[var(--color-status-error-text)] hover:underline transition-colors duration-200\"\n >\n {tr(\"auth.mfaSetup.dismiss\", \"Dismiss\")}\n </button>\n </div>\n </div>\n )}\n\n {setupData ? (\n /* Setup Flow */\n <div className=\"p-6 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-sm\">\n <div className=\"flex items-center gap-3 mb-6\">\n <div className=\"w-10 h-10 rounded-full bg-[var(--color-bg-accent)] flex items-center justify-center\">\n <svg\n className=\"w-5 h-5 text-[var(--color-text-link)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <div>\n <h2 className=\"text-xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\"auth.mfaSetup.setupAuthApp\", \"Setup Authenticator App\")}\n </h2>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr(\n \"auth.mfaSetup.scanQr\",\n \"Scan the QR code with your authenticator app\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"space-y-6\">\n {/* QR Code */}\n {setupData.qrCode && (\n <div className=\"flex justify-center\">\n <div className=\"p-4 bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)]\">\n <QRCodeSVG\n value={setupData.qrCode}\n size={192}\n level=\"M\"\n includeMargin={false}\n className=\"w-48 h-48\"\n />\n </div>\n </div>\n )}\n\n {/* Manual Secret */}\n {setupData.secret && (\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)] mb-2\">\n {tr(\n \"auth.mfaSetup.cantScan\",\n \"Can't scan the QR code? Enter this key manually:\",\n )}\n </p>\n <code className=\"block p-3 rounded-lg bg-[var(--color-bg-muted)] text-[var(--color-text-primary)] font-mono text-sm break-all select-all\">\n {setupData.secret}\n </code>\n </div>\n )}\n\n {/* Backup Codes */}\n {setupData.backupCodes && setupData.backupCodes.length > 0 && (\n <div className=\"p-4 rounded-xl bg-[var(--color-status-warning-bg-subtle)] border border-[var(--color-status-warning-border)]\">\n <div className=\"flex items-start gap-3 mb-3\">\n <svg\n className=\"w-5 h-5 text-[var(--color-status-warning-text)] flex-shrink-0 mt-0.5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n <div>\n <h3 className=\"font-medium text-[var(--color-status-warning-text)]\">\n {tr(\n \"auth.mfaSetup.saveBackupCodes\",\n \"Save your backup codes\",\n )}\n </h3>\n <p className=\"text-sm text-[var(--color-status-warning-text)]\">\n {tr(\n \"auth.mfaSetup.storeCodesSafely\",\n \"Store these codes safely. You can use them to access your account if you lose your authenticator device.\",\n )}\n </p>\n </div>\n </div>\n <div className=\"p-3 rounded-lg bg-[var(--color-bg-surface)] grid grid-cols-2 gap-2 mb-3\">\n {setupData.backupCodes.map((code, i) => (\n <code\n key={i}\n className=\"text-sm text-[var(--color-text-primary)] font-mono\"\n >\n {i + 1}. {code}\n </code>\n ))}\n </div>\n <button\n onClick={copyBackupCodes}\n className=\"text-sm font-medium text-[var(--color-status-warning-text)] hover:underline transition-colors duration-200\"\n >\n {backupCodesCopied\n ? tr(\"auth.mfaSetup.copied\", \"Copied!\")\n : tr(\"auth.mfaSetup.copyBackupCodes\", \"Copy backup codes\")}\n </button>\n </div>\n )}\n\n {/* Verification Code Input */}\n <div>\n <label className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-2\">\n {tr(\n \"auth.mfaSetup.enterCode\",\n \"Enter the 6-digit code from your app\",\n )}\n </label>\n <input\n type=\"text\"\n inputMode=\"numeric\"\n maxLength={6}\n value={verificationCode}\n onChange={(e) =>\n setVerificationCode(e.target.value.replace(/\\D/g, \"\"))\n }\n placeholder=\"000000\"\n className=\"block w-full px-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] text-center text-2xl tracking-widest font-mono focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all duration-300\"\n />\n </div>\n\n {/* Action Buttons */}\n <div className=\"flex gap-3\">\n <button\n onClick={cancelSetup}\n className=\"flex-1 py-3 px-4 rounded-xl border border-[var(--color-border-default)] text-[var(--color-text-secondary)] font-medium hover:bg-[var(--color-bg-muted)] transition-colors duration-200\"\n >\n {tr(\"auth.mfaSetup.cancel\", \"Cancel\")}\n </button>\n <button\n onClick={handleVerifyAndEnable}\n disabled={isLoading || verificationCode.length !== 6}\n className=\"auth-primary-button flex flex-1 items-center justify-center gap-2 px-4 py-3 font-medium\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"w-5 h-5 animate-spin\" />\n <span>Verifying...</span>\n </>\n ) : (\n tr(\"auth.mfaSetup.enable2fa\", \"Enable 2FA\")\n )}\n </button>\n </div>\n </div>\n </div>\n ) : (\n /* Status Display */\n <div className=\"p-6 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-sm\">\n {!user?.mfaEnabled ? (\n /* MFA Not Enabled */\n <>\n <div className=\"flex items-center gap-4 mb-6\">\n <div className=\"w-12 h-12 rounded-full bg-[var(--color-bg-muted)] flex items-center justify-center\">\n <svg\n className=\"w-6 h-6 text-[var(--color-text-tertiary)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z\"\n />\n </svg>\n </div>\n <div>\n <h2 className=\"text-xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\n \"auth.mfaSetup.twoFactorOff\",\n \"Two-Factor Authentication is Off\",\n )}\n </h2>\n <p className=\"text-[var(--color-text-secondary)]\">\n {tr(\n \"auth.mfaSetup.notProtected\",\n \"Your account is not protected with 2FA\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"mb-6 p-4 rounded-lg bg-[var(--color-bg-accent)] border border-[var(--color-border-default)]\">\n <h3 className=\"font-medium text-[var(--color-text-link)] mb-2\">\n {tr(\"auth.mfaSetup.whyEnable\", \"Why enable 2FA?\")}\n </h3>\n <ul className=\"text-sm text-[var(--color-text-link)] space-y-1\">\n <li>\n • Protects your account even if your password is compromised\n </li>\n <li>• Adds a second verification step during sign-in</li>\n <li>\n • Works with popular authenticator apps (Google, Authy,\n etc.)\n </li>\n </ul>\n </div>\n\n <button\n onClick={() => handleSetupMfa(\"TOTP\")}\n disabled={isLoading}\n className=\"auth-primary-button flex w-full items-center justify-center gap-2 px-4 py-3 font-medium\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"w-5 h-5 animate-spin\" />\n <span>Setting up...</span>\n </>\n ) : (\n <>\n <svg\n className=\"w-5 h-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n <span>\n {tr(\n \"auth.mfaSetup.enableTwoFactor\",\n \"Enable Two-Factor Authentication\",\n )}\n </span>\n </>\n )}\n </button>\n </>\n ) : (\n /* MFA Enabled */\n <>\n <div className=\"flex items-center gap-4 mb-6\">\n <div className=\"w-12 h-12 rounded-full bg-[var(--color-status-success-bg-subtle)] flex items-center justify-center\">\n <svg\n className=\"w-6 h-6 text-[var(--color-status-success-text)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z\"\n />\n </svg>\n </div>\n <div>\n <h2 className=\"text-xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\n \"auth.mfaSetup.twoFactorOn\",\n \"Two-Factor Authentication is On\",\n )}\n </h2>\n <p className=\"text-[var(--color-text-secondary)]\">\n {tr(\n \"auth.mfaSetup.protected\",\n \"Your account is protected with an authenticator app\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"mb-6 p-4 rounded-lg bg-[var(--color-status-success-bg-subtle)] border border-[var(--color-status-success-border)]\">\n <p className=\"text-sm text-[var(--color-status-success-text)]\">\n {tr(\n \"auth.mfaSetup.verificationOnSignIn\",\n \"You'll be asked for a verification code from your authenticator app when signing in.\",\n )}\n </p>\n </div>\n\n <button\n onClick={() => setShowDisableDialog(true)}\n disabled={isLoading}\n className=\"w-full py-3 px-4 rounded-xl border border-[var(--color-status-error-border)] text-[var(--color-status-error-text)] font-medium bg-[var(--color-bg-surface)] hover:bg-[var(--color-status-error-bg-subtle)] disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200 flex items-center justify-center gap-2\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"w-5 h-5 animate-spin\" />\n <span>Disabling...</span>\n </>\n ) : (\n tr(\n \"auth.mfaSetup.disableTwoFactor\",\n \"Disable Two-Factor Authentication\",\n )\n )}\n </button>\n </>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MfaSetupPage.js","names":[],"sources":["../../../src/auth/pages/MfaSetupPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { QRCodeSVG } from \"qrcode.react\";\nimport { useMfaOperations } from \"../hooks\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { ConfirmActionDialog } from \"../components/ConfirmActionDialog\";\nimport { SpinnerIcon, AlertIcon } from \"../components/AuthIcons\";\nimport type { MFAMethod, MFASetupResult } from \"../types\";\nimport {\n nativeCopyText,\n nativeImpact,\n nativeNotify,\n} from \"../../utils/nativeBridge\";\n\n/**\n * MFA setup and management page with professional UI\n */\nexport function MfaSetupPage() {\n const { 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 { setupMFA, enableMFA, disableMFA, isLoading } = useMfaOperations();\n const user = useAuthStore((state) => state.user);\n const isAuthenticated = useAuthStore((state) => state.isAuthenticated);\n const [setupData, setSetupData] = useState<MFASetupResult | null>(null);\n const [verificationCode, setVerificationCode] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [backupCodesCopied, setBackupCodesCopied] = useState(false);\n const [showDisableDialog, setShowDisableDialog] = useState(false);\n\n const handleSetupMfa = async (method: MFAMethod) => {\n try {\n setError(null);\n setSuccessMessage(null);\n const data = await setupMFA(method);\n setSetupData(data);\n } catch (err) {\n console.error(\"Failed to setup MFA:\", err);\n setError(\n err instanceof Error\n ? err.message\n : \"Failed to setup MFA. Please try again.\",\n );\n }\n };\n\n const handleVerifyAndEnable = async () => {\n if (verificationCode.length !== 6) {\n setError(tr(\"auth.mfaSetup.enter6Digit\", \"Please enter a 6-digit code\"));\n return;\n }\n\n try {\n setError(null);\n const success = await enableMFA(verificationCode);\n if (success) {\n setSetupData(null);\n setVerificationCode(\"\");\n setSuccessMessage(\n tr(\n \"auth.mfaSetup.enableSuccess\",\n \"Two-factor authentication has been enabled successfully!\",\n ),\n );\n }\n } catch (err) {\n console.error(\"Failed to verify MFA code:\", err);\n setError(\n err instanceof Error\n ? err.message\n : \"Invalid verification code. Please try again.\",\n );\n }\n };\n\n const handleDisableMfa = async () => {\n try {\n setError(null);\n setSuccessMessage(null);\n await disableMFA();\n setSuccessMessage(\n tr(\n \"auth.mfaSetup.disableSuccess\",\n \"Two-factor authentication has been disabled.\",\n ),\n );\n setShowDisableDialog(false);\n } catch (err) {\n console.error(\"Failed to disable MFA:\", err);\n setError(\n err instanceof Error\n ? err.message\n : \"Failed to disable MFA. Please try again.\",\n );\n }\n };\n\n const copyBackupCodes = async () => {\n if (setupData?.backupCodes) {\n void nativeImpact(\"light\");\n const ok = await nativeCopyText(setupData.backupCodes.join(\"\\n\"));\n if (ok) {\n setBackupCodesCopied(true);\n void nativeNotify(\"success\");\n setTimeout(() => setBackupCodesCopied(false), 2000);\n } else {\n void nativeNotify(\"error\");\n }\n }\n };\n\n const cancelSetup = () => {\n setSetupData(null);\n setVerificationCode(\"\");\n setError(null);\n };\n\n // Authentication check\n if (!isAuthenticated) {\n return (\n <div className=\"max-w-2xl mx-auto p-6\">\n <div className=\"text-center py-16 border-2 border-dashed border-border-default rounded-xl\">\n <div className=\"w-16 h-16 mx-auto mb-4 rounded-full bg-status-warning-bg-subtle flex items-center justify-center\">\n <AlertIcon className=\"w-8 h-8 text-status-warning-text\" />\n </div>\n <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr(\"auth.mfaSetup.authRequired\", \"Authentication Required\")}\n </h2>\n <p className=\"text-text-secondary\">\n {tr(\n \"auth.mfaSetup.signInToManage\",\n \"Please sign in to manage MFA settings\",\n )}\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"max-w-2xl mx-auto p-6\">\n <ConfirmActionDialog\n open={showDisableDialog}\n onOpenChange={setShowDisableDialog}\n title={tr(\n \"auth.mfaSetup.disableTitle\",\n \"Disable two-factor authentication?\",\n )}\n description={tr(\n \"auth.mfaSetup.disableDesc\",\n \"Your account will become less secure and future sign-ins will only require your password until MFA is re-enabled.\",\n )}\n confirmText={tr(\"auth.mfaSetup.disableConfirm\", \"Disable MFA\")}\n variant=\"warning\"\n isPending={isLoading}\n onConfirm={handleDisableMfa}\n />\n {/* Header */}\n <div className=\"mb-8\">\n <h1 className=\"text-3xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\"auth.mfaSetup.title\", \"Two-Factor Authentication\")}\n </h1>\n <p className=\"text-[var(--color-text-secondary)] mt-2\">\n {tr(\n \"auth.mfaSetup.subtitle\",\n \"Add an extra layer of security to your account\",\n )}\n </p>\n </div>\n\n {/* Success Message */}\n {successMessage && (\n <div className=\"mb-6 p-4 rounded-xl bg-[var(--color-status-success-bg-subtle)] border border-[var(--color-status-success-border)]\">\n <div className=\"flex items-center gap-3\">\n <svg\n className=\"w-5 h-5 text-[var(--color-status-success-text)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <p className=\"text-[var(--color-status-success-text)]\">\n {successMessage}\n </p>\n </div>\n </div>\n )}\n\n {/* Error Message */}\n {error && (\n <div className=\"mb-6 flex items-start gap-3 p-4 rounded-xl bg-[var(--color-status-error-bg-subtle)] border border-[var(--color-status-error-border)]\">\n <AlertIcon className=\"w-5 h-5 text-[var(--color-status-error-text)] flex-shrink-0 mt-0.5\" />\n <div className=\"flex-1\">\n <p className=\"text-sm text-[var(--color-status-error-text)]\">\n {error}\n </p>\n <button\n onClick={() => setError(null)}\n className=\"mt-2 text-sm text-[var(--color-status-error-text)] hover:underline transition-colors duration-200\"\n >\n {tr(\"auth.mfaSetup.dismiss\", \"Dismiss\")}\n </button>\n </div>\n </div>\n )}\n\n {setupData ? (\n /* Setup Flow */\n <div className=\"p-6 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-sm\">\n <div className=\"flex items-center gap-3 mb-6\">\n <div className=\"w-10 h-10 rounded-full bg-[var(--color-bg-accent)] flex items-center justify-center\">\n <svg\n className=\"w-5 h-5 text-[var(--color-text-link)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <div>\n <h2 className=\"text-xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\"auth.mfaSetup.setupAuthApp\", \"Setup Authenticator App\")}\n </h2>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {tr(\n \"auth.mfaSetup.scanQr\",\n \"Scan the QR code with your authenticator app\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"space-y-6\">\n {/* QR Code */}\n {setupData.qrCode && (\n <div className=\"flex justify-center\">\n <div className=\"p-4 bg-[var(--color-bg-surface)] rounded-xl border border-[var(--color-border-default)]\">\n <QRCodeSVG\n value={setupData.qrCode}\n size={192}\n level=\"M\"\n includeMargin={false}\n className=\"w-48 h-48\"\n />\n </div>\n </div>\n )}\n\n {/* Manual Secret */}\n {setupData.secret && (\n <div>\n <p className=\"text-sm text-[var(--color-text-secondary)] mb-2\">\n {tr(\n \"auth.mfaSetup.cantScan\",\n \"Can't scan the QR code? Enter this key manually:\",\n )}\n </p>\n <code className=\"block p-3 rounded-lg bg-[var(--color-bg-muted)] text-[var(--color-text-primary)] font-mono text-sm break-all select-all\">\n {setupData.secret}\n </code>\n </div>\n )}\n\n {/* Backup Codes */}\n {setupData.backupCodes && setupData.backupCodes.length > 0 && (\n <div className=\"p-4 rounded-xl bg-[var(--color-status-warning-bg-subtle)] border border-[var(--color-status-warning-border)]\">\n <div className=\"flex items-start gap-3 mb-3\">\n <svg\n className=\"w-5 h-5 text-[var(--color-status-warning-text)] flex-shrink-0 mt-0.5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n <div>\n <h3 className=\"font-medium text-[var(--color-status-warning-text)]\">\n {tr(\n \"auth.mfaSetup.saveBackupCodes\",\n \"Save your backup codes\",\n )}\n </h3>\n <p className=\"text-sm text-[var(--color-status-warning-text)]\">\n {tr(\n \"auth.mfaSetup.storeCodesSafely\",\n \"Store these codes safely. You can use them to access your account if you lose your authenticator device.\",\n )}\n </p>\n </div>\n </div>\n <div className=\"p-3 rounded-lg bg-[var(--color-bg-surface)] grid grid-cols-2 gap-2 mb-3\">\n {setupData.backupCodes.map((code, i) => (\n <code\n key={i}\n className=\"text-sm text-[var(--color-text-primary)] font-mono\"\n >\n {i + 1}. {code}\n </code>\n ))}\n </div>\n <button\n onClick={copyBackupCodes}\n className=\"text-sm font-medium text-[var(--color-status-warning-text)] hover:underline transition-colors duration-200\"\n >\n {backupCodesCopied\n ? tr(\"auth.mfaSetup.copied\", \"Copied!\")\n : tr(\"auth.mfaSetup.copyBackupCodes\", \"Copy backup codes\")}\n </button>\n </div>\n )}\n\n {/* Verification Code Input */}\n <div>\n <label className=\"block text-sm font-medium text-[var(--color-text-primary)] mb-2\">\n {tr(\n \"auth.mfaSetup.enterCode\",\n \"Enter the 6-digit code from your app\",\n )}\n </label>\n <input\n type=\"text\"\n inputMode=\"numeric\"\n maxLength={6}\n value={verificationCode}\n onChange={(e) =>\n setVerificationCode(e.target.value.replace(/\\D/g, \"\"))\n }\n placeholder=\"000000\"\n className=\"block w-full px-4 py-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] text-center text-2xl tracking-widest font-mono focus:outline-none focus:ring-2 focus:ring-action-primary-bg/20 focus:border-action-primary-bg transition-all duration-300\"\n />\n </div>\n\n {/* Action Buttons */}\n <div className=\"flex gap-3\">\n <button\n onClick={cancelSetup}\n className=\"flex-1 py-3 px-4 rounded-xl border border-[var(--color-border-default)] text-[var(--color-text-secondary)] font-medium hover:bg-[var(--color-bg-muted)] transition-colors duration-200\"\n >\n {tr(\"auth.mfaSetup.cancel\", \"Cancel\")}\n </button>\n <button\n onClick={handleVerifyAndEnable}\n disabled={isLoading || verificationCode.length !== 6}\n className=\"auth-primary-button flex flex-1 items-center justify-center gap-2 px-4 py-3 font-medium\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"w-5 h-5 animate-spin\" />\n <span>Verifying...</span>\n </>\n ) : (\n tr(\"auth.mfaSetup.enable2fa\", \"Enable 2FA\")\n )}\n </button>\n </div>\n </div>\n </div>\n ) : (\n /* Status Display */\n <div className=\"p-6 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] shadow-sm\">\n {!user?.mfaEnabled ? (\n /* MFA Not Enabled */\n <>\n <div className=\"flex items-center gap-4 mb-6\">\n <div className=\"w-12 h-12 rounded-full bg-[var(--color-bg-muted)] flex items-center justify-center\">\n <svg\n className=\"w-6 h-6 text-[var(--color-text-tertiary)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z\"\n />\n </svg>\n </div>\n <div>\n <h2 className=\"text-xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\n \"auth.mfaSetup.twoFactorOff\",\n \"Two-Factor Authentication is Off\",\n )}\n </h2>\n <p className=\"text-[var(--color-text-secondary)]\">\n {tr(\n \"auth.mfaSetup.notProtected\",\n \"Your account is not protected with 2FA\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"mb-6 p-4 rounded-lg bg-[var(--color-bg-accent)] border border-[var(--color-border-default)]\">\n <h3 className=\"font-medium text-[var(--color-text-link)] mb-2\">\n {tr(\"auth.mfaSetup.whyEnable\", \"Why enable 2FA?\")}\n </h3>\n <ul className=\"text-sm text-[var(--color-text-link)] space-y-1\">\n <li>\n • Protects your account even if your password is compromised\n </li>\n <li>• Adds a second verification step during sign-in</li>\n <li>\n • Works with popular authenticator apps (Google, Authy,\n etc.)\n </li>\n </ul>\n </div>\n\n <button\n onClick={() => handleSetupMfa(\"TOTP\")}\n disabled={isLoading}\n className=\"auth-primary-button flex w-full items-center justify-center gap-2 px-4 py-3 font-medium\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"w-5 h-5 animate-spin\" />\n <span>Setting up...</span>\n </>\n ) : (\n <>\n <svg\n className=\"w-5 h-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n <span>\n {tr(\n \"auth.mfaSetup.enableTwoFactor\",\n \"Enable Two-Factor Authentication\",\n )}\n </span>\n </>\n )}\n </button>\n </>\n ) : (\n /* MFA Enabled */\n <>\n <div className=\"flex items-center gap-4 mb-6\">\n <div className=\"w-12 h-12 rounded-full bg-[var(--color-status-success-bg-subtle)] flex items-center justify-center\">\n <svg\n className=\"w-6 h-6 text-[var(--color-status-success-text)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z\"\n />\n </svg>\n </div>\n <div>\n <h2 className=\"text-xl font-semibold text-[var(--color-text-primary)]\">\n {tr(\n \"auth.mfaSetup.twoFactorOn\",\n \"Two-Factor Authentication is On\",\n )}\n </h2>\n <p className=\"text-[var(--color-text-secondary)]\">\n {tr(\n \"auth.mfaSetup.protected\",\n \"Your account is protected with an authenticator app\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"mb-6 p-4 rounded-lg bg-[var(--color-status-success-bg-subtle)] border border-[var(--color-status-success-border)]\">\n <p className=\"text-sm text-[var(--color-status-success-text)]\">\n {tr(\n \"auth.mfaSetup.verificationOnSignIn\",\n \"You'll be asked for a verification code from your authenticator app when signing in.\",\n )}\n </p>\n </div>\n\n <button\n onClick={() => setShowDisableDialog(true)}\n disabled={isLoading}\n className=\"w-full py-3 px-4 rounded-xl border border-[var(--color-status-error-border)] text-[var(--color-status-error-text)] font-medium bg-[var(--color-bg-surface)] hover:bg-[var(--color-status-error-bg-subtle)] disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-200 flex items-center justify-center gap-2\"\n >\n {isLoading ? (\n <>\n <SpinnerIcon className=\"w-5 h-5 animate-spin\" />\n <span>Disabling...</span>\n </>\n ) : (\n tr(\n \"auth.mfaSetup.disableTwoFactor\",\n \"Disable Two-Factor Authentication\",\n )\n )}\n </button>\n </>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityPage.d.ts","sourceRoot":"","sources":["../../../src/auth/pages/SecurityPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SecurityPage.d.ts","sourceRoot":"","sources":["../../../src/auth/pages/SecurityPage.tsx"],"names":[],"mappings":"AA4MA;;GAEG;AACH,wBAAgB,YAAY,4CA8tB3B"}
|