import { useState } from "react"; import { toast } from "sonner"; import { QRCodeCanvas } from "qrcode.react"; import { Button } from "~/client/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "~/client/components/ui/dialog"; import { Input } from "~/client/components/ui/input"; import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from "~/client/components/ui/input-otp"; import { Label } from "~/client/components/ui/label"; import { authClient } from "~/client/lib/auth-client"; type TwoFactorSetupDialogProps = { open: boolean; onOpenChange: (open: boolean) => void; onSuccess: () => void; }; export const TwoFactorSetupDialog = ({ open, onOpenChange, onSuccess }: TwoFactorSetupDialogProps) => { const [setupStep, setSetupStep] = useState<"password" | "qr" | "verify">("password"); const [password, setPassword] = useState(""); const [totpUri, setTotpUri] = useState(null); const [verificationCode, setVerificationCode] = useState(""); const [backupCodes, setBackupCodes] = useState([]); const [isEnabling2FA, setIsEnabling2FA] = useState(false); const [isVerifying2FA, setIsVerifying2FA] = useState(false); const handleEnable2FA = async (e: React.FormEvent) => { e.preventDefault(); if (!password) { toast.error("Password is required"); return; } const { data, error } = await authClient.twoFactor.enable({ password, issuer: "Zerobyte", fetchOptions: { onRequest: () => { setIsEnabling2FA(true); }, onResponse: () => { setIsEnabling2FA(false); }, }, }); if (error) { console.error(error); toast.error("Failed to enable 2FA", { description: error.message }); return; } setTotpUri(data.totpURI); setBackupCodes(data.backupCodes); setSetupStep("qr"); }; const handleVerify2FA = async () => { if (verificationCode.length !== 6) { toast.error("Please enter a 6-digit code"); return; } const { data, error } = await authClient.twoFactor.verifyTotp({ code: verificationCode, fetchOptions: { onRequest: () => { setIsVerifying2FA(true); }, onResponse: () => { setIsVerifying2FA(false); }, }, }); if (error) { console.error(error); toast.error("Verification failed", { description: error.message }); setVerificationCode(""); return; } if (data) { toast.success("Two-factor authentication enabled successfully"); handleClose(); onSuccess(); } }; const handleClose = () => { onOpenChange(false); setTimeout(() => { setPassword(""); setTotpUri(null); setVerificationCode(""); setBackupCodes([]); setSetupStep("password"); }, 200); }; return ( {setupStep === "password" && (
Enable Two-Factor Authentication Enter your password to generate a QR code for your authenticator app
setPassword(e.target.value)} placeholder="Enter your password" required />
)} {setupStep === "qr" && totpUri && ( <> Scan QR Code Scan this QR code with your authenticator app (Google Authenticator, Authy, etc.)
{backupCodes.length > 0 && (
{backupCodes.map((code) => (
{code}
))}
)}
)} {setupStep === "verify" && ( <> Verify setup Enter the 6-digit code from your authenticator app to complete setup
)}
); };