mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-05 14:26:38 +00:00
Addressed PR comments
This commit is contained in:
@@ -56,7 +56,7 @@ export const registerMfaRouter = async (server: FastifyZodProvider) => {
|
|||||||
const cfg = getConfig();
|
const cfg = getConfig();
|
||||||
|
|
||||||
server.decorateRequest("mfa", null);
|
server.decorateRequest("mfa", null);
|
||||||
server.addHook("preParsing", async (req, res) => {
|
server.addHook("preValidation", async (req, res) => {
|
||||||
const authorizationHeader = req.headers.authorization;
|
const authorizationHeader = req.headers.authorization;
|
||||||
|
|
||||||
if (!authorizationHeader || !authorizationHeader.startsWith("Bearer ")) {
|
if (!authorizationHeader || !authorizationHeader.startsWith("Bearer ")) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import Error from "@app/components/basic/Error";
|
|||||||
import TotpRegistration from "@app/components/mfa/TotpRegistration";
|
import TotpRegistration from "@app/components/mfa/TotpRegistration";
|
||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
import SecurityClient from "@app/components/utilities/SecurityClient";
|
||||||
import { Button } from "@app/components/v2";
|
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 { checkUserTotpMfa, verifyMfaToken, verifyRecoveryCode } from "@app/hooks/api/auth/queries";
|
||||||
import { MfaMethod } from "@app/hooks/api/auth/types";
|
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 [isLoadingResend, setIsLoadingResend] = useState(false);
|
||||||
const [triesLeft, setTriesLeft] = useState<number | undefined>(undefined);
|
const [triesLeft, setTriesLeft] = useState<number | undefined>(undefined);
|
||||||
const [shouldShowTotpRegistration, setShouldShowTotpRegistration] = useState(false);
|
const [shouldShowTotpRegistration, setShouldShowTotpRegistration] = useState(false);
|
||||||
|
const logout = useLogoutUser(true);
|
||||||
|
|
||||||
const sendMfaToken = useSendMfaToken();
|
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<HTMLFormElement>) => {
|
const verifyMfa = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
if (!mfaCode.trim()) return;
|
if (!mfaCode.trim() || !isCodeComplete) return;
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
@@ -107,16 +116,18 @@ export const Mfa = ({ successCallback, closeMfa, hideLogo, email, method }: Prop
|
|||||||
closeMfa();
|
closeMfa();
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
if (triesLeft) {
|
if (typeof triesLeft === "number") {
|
||||||
setTriesLeft((left) => {
|
const newTriesLeft = triesLeft - 1;
|
||||||
if (triesLeft === 1) {
|
setTriesLeft(newTriesLeft);
|
||||||
navigate({ to: "/" });
|
if (newTriesLeft <= 0) {
|
||||||
|
setIsLoading(false);
|
||||||
SecurityClient.setMfaToken("");
|
SecurityClient.setMfaToken("");
|
||||||
SecurityClient.setToken("");
|
SecurityClient.setToken("");
|
||||||
}
|
SecurityClient.setSignupToken("");
|
||||||
return (left as number) - 1;
|
await logout.mutateAsync();
|
||||||
});
|
navigate({ to: "/login" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setTriesLeft(2);
|
setTriesLeft(2);
|
||||||
}
|
}
|
||||||
@@ -251,6 +262,7 @@ export const Mfa = ({ successCallback, closeMfa, hideLogo, email, method }: Prop
|
|||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
isDisabled={!isCodeComplete}
|
||||||
>
|
>
|
||||||
{String(t("mfa.verify"))}
|
{String(t("mfa.verify"))}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -263,7 +275,6 @@ export const Mfa = ({ successCallback, closeMfa, hideLogo, email, method }: Prop
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowRecoveryCodeInput(!showRecoveryCodeInput);
|
setShowRecoveryCodeInput(!showRecoveryCodeInput);
|
||||||
setMfaCode("");
|
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"
|
className="text-bunker-400 transition-colors duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -21,16 +21,7 @@ export const RecoveryCodesDownload = ({
|
|||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
||||||
const downloadRecoveryCodes = () => {
|
const downloadRecoveryCodes = () => {
|
||||||
const content = [
|
const content = [...recoveryCodes].join("\n");
|
||||||
"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 blob = new Blob([content], { type: "text/plain" });
|
const blob = new Blob([content], { type: "text/plain" });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
@@ -75,8 +66,8 @@ export const RecoveryCodesDownload = ({
|
|||||||
<div className="grid grid-cols-2 gap-x-6 gap-y-2 font-mono text-sm">
|
<div className="grid grid-cols-2 gap-x-6 gap-y-2 font-mono text-sm">
|
||||||
{recoveryCodes.map((code, index) => (
|
{recoveryCodes.map((code, index) => (
|
||||||
<div key={code} className="flex items-center text-mineshaft-200">
|
<div key={code} className="flex items-center text-mineshaft-200">
|
||||||
<span className="w-6 text-mineshaft-400">{index + 1}.</span>
|
<span className="w-8 text-right text-mineshaft-400">{index + 1}.</span>
|
||||||
<span>{code}</span>
|
<span className="pl-2">{code}</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user