import React, { useState } from "react"; import { Container, Title, Text, PinInput, Button, Stack, Anchor, Paper, Center, ThemeIcon, } from "@mantine/core"; import { useForm } from "@mantine/form"; import { zod4Resolver } from "mantine-form-zod-resolver"; import { IconDeviceMobile, IconLock } from "@tabler/icons-react"; import { useNavigate } from "react-router-dom"; import { notifications } from "@mantine/notifications"; import classes from "./mfa-challenge.module.css"; import { verifyMfa } from "@/ee/mfa"; import APP_ROUTE, { getPostLoginRedirect } from "@/lib/app-route"; import { useTranslation } from "react-i18next"; import { z } from "zod/v4"; import { MfaBackupCodeInput } from "./mfa-backup-code-input"; import { AuthLayout } from "@/features/auth/components/auth-layout.tsx"; const formSchema = z.object({ code: z .string() .refine( (val) => (val.length === 6 && /^\d{6}$/.test(val)) || val.length === 8, { message: "Enter a 6-digit code or 8-character backup code", }, ), }); type MfaChallengeFormValues = z.infer; export function MfaChallenge() { const { t } = useTranslation(); const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(false); const [useBackupCode, setUseBackupCode] = useState(false); const form = useForm({ validate: zod4Resolver(formSchema), initialValues: { code: "", }, }); const handleSubmit = async (values: MfaChallengeFormValues) => { setIsLoading(true); try { await verifyMfa(values.code); navigate(getPostLoginRedirect()); } catch (error: any) { setIsLoading(false); notifications.show({ message: error.response?.data?.message || t("Invalid verification code"), color: "red", }); form.setFieldValue("code", ""); } }; return (
{t("Two-factor authentication")} {useBackupCode ? t("Enter one of your backup codes") : t("Enter the 6-digit code found in your authenticator app")} {!useBackupCode ? (
{form.errors.code && ( {form.errors.code} )} { setUseBackupCode(true); form.setFieldValue("code", ""); form.clearErrors(); }} > {t("Use backup code")}
) : ( form.setFieldValue("code", value)} error={form.errors.code?.toString()} onSubmit={() => handleSubmit(form.values)} onCancel={() => { setUseBackupCode(false); form.setFieldValue("code", ""); form.clearErrors(); }} isLoading={isLoading} /> )}
); }