import React, { useState } from "react"; import { Alert, Box, Button, Checkbox, FormControlLabel, TextField, Typography } from "@mui/material"; import { api, getApiErrorMessage } from "../api"; import { useI18n } from "../i18n/I18nProvider"; type ChallengeResponse = { authenticated: true; provider: "local" }; export default function TwoFactorChallenge({ pendingToken, onSuccess, onCancel, }: { pendingToken: string; onSuccess: (data: ChallengeResponse) => void; onCancel: () => void; }) { const { t } = useI18n(); const [code, setCode] = useState(""); const [trustDevice, setTrustDevice] = useState(false); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); async function submit() { setLoading(true); setError(null); try { const res = await api.post("/auth/2fa/challenge", { pendingToken, code, trustDevice }); onSuccess(res.data); } catch (e: any) { if (e?.response?.status === 429) { setError(t("twoFactorRateLimited")); } else { setError(getApiErrorMessage(e, t("twoFactorInvalidCode"))); } } finally { setLoading(false); } } return ( { e.preventDefault(); void submit(); }} sx={{ display: "flex", flexDirection: "column", gap: 1.5 }} role="group" aria-label={t("twoFactorTitle")} > {t("twoFactorTitle")} {t("twoFactorHint")} {error ? {error} : null} setCode(e.target.value)} autoComplete="one-time-code" autoFocus fullWidth /> setTrustDevice(e.target.checked)} />} label={t("twoFactorTrustDevice")} /> ); }