import type { BaseSyntheticEvent } from "react"; import React, { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { ErrorCode } from "@calcom/features/auth/lib/ErrorCode"; import { useCallbackRef } from "@calcom/lib/hooks/useCallbackRef"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Button, Dialog, DialogContent, Form } from "@calcom/ui"; import TwoFactor from "@components/auth/TwoFactor"; import TwoFactorAuthAPI from "./TwoFactorAuthAPI"; import TwoFactorModalHeader from "./TwoFactorModalHeader"; interface EnableTwoFactorModalProps { /** * Called when the user closes the modal without disabling two-factor auth */ onCancel: () => void; /** * Called when the user enables two-factor auth */ onEnable: () => void; } enum SetupStep { ConfirmPassword, DisplayQrCode, EnterTotpCode, } const WithStep = ({ step, current, children, }: { step: SetupStep; current: SetupStep; children: JSX.Element; }) => { return step === current ? children : null; }; interface EnableTwoFactorValues { totpCode: string; } const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps) => { const { t } = useLocale(); const form = useForm(); const setupDescriptions = { [SetupStep.ConfirmPassword]: t("2fa_confirm_current_password"), [SetupStep.DisplayQrCode]: t("2fa_scan_image_or_use_code"), [SetupStep.EnterTotpCode]: t("2fa_enter_six_digit_code"), }; const [step, setStep] = useState(SetupStep.ConfirmPassword); const [password, setPassword] = useState(""); const [dataUri, setDataUri] = useState(""); const [secret, setSecret] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); const [errorMessage, setErrorMessage] = useState(null); async function handleSetup(e: React.FormEvent) { e.preventDefault(); if (isSubmitting) { return; } setIsSubmitting(true); setErrorMessage(null); try { const response = await TwoFactorAuthAPI.setup(password); const body = await response.json(); if (response.status === 200) { setDataUri(body.dataUri); setSecret(body.secret); setStep(SetupStep.DisplayQrCode); return; } if (body.error === ErrorCode.IncorrectPassword) { setErrorMessage(t("incorrect_password")); } else { setErrorMessage(t("something_went_wrong")); } } catch (e) { setErrorMessage(t("something_went_wrong")); console.error(t("error_enabling_2fa"), e); } finally { setIsSubmitting(false); } } async function handleEnable({ totpCode }: EnableTwoFactorValues, e: BaseSyntheticEvent | undefined) { e?.preventDefault(); if (isSubmitting) { return; } setIsSubmitting(true); setErrorMessage(null); try { const response = await TwoFactorAuthAPI.enable(totpCode); const body = await response.json(); if (response.status === 200) { onEnable(); return; } if (body.error === ErrorCode.IncorrectTwoFactorCode) { setErrorMessage(`${t("code_is_incorrect")} ${t("please_try_again")}`); } else { setErrorMessage(t("something_went_wrong")); } } catch (e) { setErrorMessage(t("something_went_wrong")); console.error(t("error_enabling_2fa"), e); } finally { setIsSubmitting(false); } } const handleEnableRef = useCallbackRef(handleEnable); const totpCode = form.watch("totpCode"); // auto submit 2FA if all inputs have a value useEffect(() => { if (totpCode?.trim().length === 6) { form.handleSubmit(handleEnableRef.current)(); } }, [form, handleEnableRef, totpCode]); return (
setPassword(e.currentTarget.value)} className="border-default block w-full rounded-sm text-sm" />
{errorMessage &&

{errorMessage}

}
<>
{ // eslint-disable-next-line @next/next/no-img-element }

{secret}

{errorMessage &&

{errorMessage}

}
); }; export default EnableTwoFactorModal;