/** * Two-factor authentication component * Copyright (c) 2024 Ervin Remus Radosavlevici * All rights reserved. */ import { useState } from 'react'; interface TwoFactorAuthProps { onVerify: (code: string) => Promise; onCancel: () => void; } export default function TwoFactorAuth({ onVerify, onCancel }: TwoFactorAuthProps) { const [code, setCode] = useState(['', '', '', '', '', '']); const [error, setError] = useState(''); const [isVerifying, setIsVerifying] = useState(false); // Handle input change for each digit const handleChange = (index: number, value: string) => { // Only allow numbers if (value && !/^\d+$/.test(value)) return; const newCode = [...code]; newCode[index] = value; setCode(newCode); // Auto-focus next input if (value && index < 5) { const nextInput = document.getElementById(`2fa-input-${index + 1}`); nextInput?.focus(); } // Clear error when typing if (error) setError(''); }; // Handle key down for backspace navigation const handleKeyDown = (index: number, e: React.KeyboardEvent) => { if (e.key === 'Backspace' && !code[index] && index > 0) { const prevInput = document.getElementById(`2fa-input-${index - 1}`); prevInput?.focus(); } }; // Handle paste event to fill all inputs const handlePaste = (e: React.ClipboardEvent) => { e.preventDefault(); const pastedData = e.clipboardData.getData('text/plain').trim(); // Check if pasted content is a valid 6-digit code if (/^\d{6}$/.test(pastedData)) { const digits = pastedData.split(''); setCode(digits); // Focus the last input const lastInput = document.getElementById('2fa-input-5'); lastInput?.focus(); } }; // Handle verification const handleVerify = async () => { const fullCode = code.join(''); // Validate code format if (fullCode.length !== 6 || !/^\d{6}$/.test(fullCode)) { setError('Please enter a valid 6-digit code'); return; } setIsVerifying(true); setError(''); try { const isValid = await onVerify(fullCode); if (!isValid) { setError('Invalid verification code. Please try again.'); setCode(['', '', '', '', '', '']); // Focus first input document.getElementById('2fa-input-0')?.focus(); } } catch (err) { setError('An error occurred during verification. Please try again.'); } finally { setIsVerifying(false); } }; return (

Two-Factor Authentication

Enter the 6-digit code from your authenticator app

{code.map((digit, index) => ( handleChange(index, e.target.value)} onKeyDown={(e) => handleKeyDown(index, e)} onPaste={index === 0 ? handlePaste : undefined} className="w-12 h-12 text-center text-xl border rounded-md focus:border-blue-500 focus:ring-1 focus:ring-blue-500" autoFocus={index === 0} /> ))}
{error && (
{error}
)}
); }