"use client"; import { useEffect, useState } from "react"; import { AppShell } from "../../../components/app-shell"; import { apiFetch } from "@/lib/api"; type ApiResponse = { data: T; meta: { timestamp: string; version: "v1" }; error: null | { message: string; code?: string }; }; type TwoFaGenerateData = { qrCode: string; otpAuthUrl: string }; type UserData = { user: { twoFactorEnabled: boolean } }; export default function TwoFAPage() { const [enabled, setEnabled] = useState(null); const [qrCode, setQrCode] = useState(""); const [otpAuthUrl, setOtpAuthUrl] = useState(""); const [token, setToken] = useState(""); const [status, setStatus] = useState(""); const [isError, setIsError] = useState(false); const [step, setStep] = useState<"idle" | "scan" | "done">("idle"); useEffect(() => { apiFetch("/api/auth/me") .then((res) => { if (!res.error && res.data) { // me returns { user: {...} } const data = res.data as unknown as UserData; setEnabled(data.user?.twoFactorEnabled ?? false); } }) .catch(() => {}); }, []); const handleGenerate = async () => { setStatus("Generating QR code..."); setIsError(false); const res = await apiFetch("/api/2fa/generate", { method: "POST" }); if (res.error) { setStatus(res.error.message ?? "Failed to generate 2FA secret."); setIsError(true); return; } setQrCode(res.data.qrCode); setOtpAuthUrl(res.data.otpAuthUrl); setStep("scan"); setStatus("Scan the QR code with your authenticator app, then enter the code below."); }; const handleEnable = async (event: React.FormEvent) => { event.preventDefault(); if (!token || token.length !== 6) { setStatus("Please enter the 6-digit code."); setIsError(true); return; } setStatus("Verifying..."); setIsError(false); const res = await apiFetch<{ message: string }>("/api/2fa/enable", { method: "POST", body: JSON.stringify({ token }), }); if (res.error) { setStatus(res.error.message ?? "Verification failed. Try again."); setIsError(true); return; } setEnabled(true); setStep("done"); setStatus("Two-factor authentication is now active."); }; const handleDisable = async (event: React.FormEvent) => { event.preventDefault(); if (!token || token.length !== 6) { setStatus("Please enter the 6-digit code to confirm."); setIsError(true); return; } setStatus("Disabling 2FA..."); setIsError(false); const res = await apiFetch<{ message: string }>("/api/2fa/disable", { method: "DELETE", body: JSON.stringify({ token }), }); if (res.error) { setStatus(res.error.message ?? "Failed. Check your authenticator code."); setIsError(true); return; } setEnabled(false); setToken(""); setStep("idle"); setStatus("Two-factor authentication has been disabled."); }; return (
{enabled === null ? (
) : enabled ? ( <>

2FA is Active

Your account is protected with TOTP.

To disable two-factor authentication, enter the current code from your authenticator app.

setToken(e.target.value.replace(/\D/g, ""))} className="block w-full rounded-lg border border-border bg-background/50 px-3 py-2 text-sm text-center tracking-widest focus:border-primary focus:outline-none focus:ring-primary transition-all" required />
) : step === "idle" ? ( <>

2FA Not Enabled

Add an extra layer of protection.

Use any TOTP authenticator app (Google Authenticator, Authy, 1Password) to generate login codes.

) : step === "scan" ? ( <>

Scan this QR code

Open your authenticator app and scan the code below, or enter the key manually.

{qrCode && (
{/* eslint-disable-next-line @next/next/no-img-element */} 2FA QR Code
)} {otpAuthUrl && (

{otpAuthUrl}

)}
setToken(e.target.value.replace(/\D/g, ""))} className="block w-full rounded-lg border border-border bg-background/50 px-3 py-2 text-sm text-center tracking-widest focus:border-primary focus:outline-none focus:ring-primary transition-all" required autoFocus />
) : (

2FA Enabled Successfully

Your account now requires a code on each login.

)} {status && (
{status}
)}
); }