From ba277a057e08f2021cf0d1618a8a6d2e8539a332 Mon Sep 17 00:00:00 2001 From: Carlos Monastyrski Date: Tue, 23 Sep 2025 09:27:14 -0300 Subject: [PATCH] Addressed PR comments --- backend/src/server/routes/v2/mfa-router.ts | 2 +- frontend/src/components/auth/Mfa.tsx | 37 ++++++++++++------- .../components/mfa/RecoveryCodesDownload.tsx | 15 ++------ 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/backend/src/server/routes/v2/mfa-router.ts b/backend/src/server/routes/v2/mfa-router.ts index e3e122a17..e8c2cea69 100644 --- a/backend/src/server/routes/v2/mfa-router.ts +++ b/backend/src/server/routes/v2/mfa-router.ts @@ -56,7 +56,7 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => { const cfg = getConfig(); server.decorateRequest("mfa", null); - server.addHook("preParsing", async (req, res) => { + server.addHook("preValidation", async (req, res) => { const authorizationHeader = req.headers.authorization; if (!authorizationHeader || !authorizationHeader.startsWith("Bearer ")) { diff --git a/frontend/src/components/auth/Mfa.tsx b/frontend/src/components/auth/Mfa.tsx index 6eab69098..d8ffc55d6 100644 --- a/frontend/src/components/auth/Mfa.tsx +++ b/frontend/src/components/auth/Mfa.tsx @@ -7,7 +7,7 @@ import Error from "@app/components/basic/Error"; import TotpRegistration from "@app/components/mfa/TotpRegistration"; import SecurityClient from "@app/components/utilities/SecurityClient"; import { Button } from "@app/components/v2"; -import { useSendMfaToken } from "@app/hooks/api"; +import { useLogoutUser, useSendMfaToken } from "@app/hooks/api"; import { checkUserTotpMfa, verifyMfaToken, verifyRecoveryCode } from "@app/hooks/api/auth/queries"; import { MfaMethod } from "@app/hooks/api/auth/types"; @@ -66,6 +66,7 @@ export const Mfa = ({ successCallback, closeMfa, hideLogo, email, method }: Prop const [isLoadingResend, setIsLoadingResend] = useState(false); const [triesLeft, setTriesLeft] = useState(undefined); const [shouldShowTotpRegistration, setShouldShowTotpRegistration] = useState(false); + const logout = useLogoutUser(true); const sendMfaToken = useSendMfaToken(); @@ -80,10 +81,18 @@ export const Mfa = ({ successCallback, closeMfa, hideLogo, email, method }: Prop } }, []); + const getExpectedCodeLength = () => { + if (method === MfaMethod.EMAIL) return 6; + if (method === MfaMethod.TOTP) return showRecoveryCodeInput ? 8 : 6; + return 6; + }; + + const isCodeComplete = mfaCode.length === getExpectedCodeLength(); + const verifyMfa = async (event: React.FormEvent) => { event.preventDefault(); - if (!mfaCode.trim()) return; + if (!mfaCode.trim() || !isCodeComplete) return; setIsLoading(true); try { @@ -107,16 +116,18 @@ export const Mfa = ({ successCallback, closeMfa, hideLogo, email, method }: Prop closeMfa(); } } catch { - if (triesLeft) { - setTriesLeft((left) => { - if (triesLeft === 1) { - navigate({ to: "/" }); - - SecurityClient.setMfaToken(""); - SecurityClient.setToken(""); - } - return (left as number) - 1; - }); + if (typeof triesLeft === "number") { + const newTriesLeft = triesLeft - 1; + setTriesLeft(newTriesLeft); + if (newTriesLeft <= 0) { + setIsLoading(false); + SecurityClient.setMfaToken(""); + SecurityClient.setToken(""); + SecurityClient.setSignupToken(""); + await logout.mutateAsync(); + navigate({ to: "/login" }); + return; + } } else { setTriesLeft(2); } @@ -251,6 +262,7 @@ export const Mfa = ({ successCallback, closeMfa, hideLogo, email, method }: Prop colorSchema="primary" variant="outline_bg" isLoading={isLoading} + isDisabled={!isCodeComplete} > {String(t("mfa.verify"))} @@ -263,7 +275,6 @@ export const Mfa = ({ successCallback, closeMfa, hideLogo, email, method }: Prop onClick={() => { setShowRecoveryCodeInput(!showRecoveryCodeInput); setMfaCode(""); - setTriesLeft(undefined); }} className="text-bunker-400 transition-colors duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4" > diff --git a/frontend/src/components/mfa/RecoveryCodesDownload.tsx b/frontend/src/components/mfa/RecoveryCodesDownload.tsx index 52739df97..50219f7dc 100644 --- a/frontend/src/components/mfa/RecoveryCodesDownload.tsx +++ b/frontend/src/components/mfa/RecoveryCodesDownload.tsx @@ -21,16 +21,7 @@ export const RecoveryCodesDownload = ({ const [copied, setCopied] = useState(false); const downloadRecoveryCodes = () => { - const content = [ - "Infisical Two-Factor Authentication Recovery Codes", - `Generated on: ${new Date().toLocaleString()}`, - "", - "Important: Store these codes in a safe place. Each code can only be used once.", - "If you lose access to your mobile authenticator, you can use these codes to regain access to your account.", - "", - "Recovery Codes:", - ...recoveryCodes.map((code, index) => `${index + 1}. ${code}`) - ].join("\n"); + const content = [...recoveryCodes].join("\n"); const blob = new Blob([content], { type: "text/plain" }); const url = URL.createObjectURL(blob); @@ -75,8 +66,8 @@ export const RecoveryCodesDownload = ({
{recoveryCodes.map((code, index) => (
- {index + 1}. - {code} + {index + 1}. + {code}
))}