'use client'; import { useState, useEffect } from 'react'; import Image from 'next/image'; import { twoFactorService, TwoFactorSetupResponse } from '@/services'; type Step = 'status' | 'setup' | 'verify' | 'backup-codes' | 'disable'; interface TwoFactorSettingsProps { onStatusChange?: (enabled: boolean) => void; } export function TwoFactorSettings({ onStatusChange }: TwoFactorSettingsProps) { const [step, setStep] = useState('status'); const [isEnabled, setIsEnabled] = useState(false); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [success, setSuccess] = useState(null); // Setup data const [setupData, setSetupData] = useState(null); const [verificationCode, setVerificationCode] = useState(''); const [backupCodes, setBackupCodes] = useState([]); const [backupCodesCopied, setBackupCodesCopied] = useState(false); // Disable form const [disablePassword, setDisablePassword] = useState(''); const [showDisablePassword, setShowDisablePassword] = useState(false); // Fetch 2FA status on mount useEffect(() => { fetchStatus(); }, []); const fetchStatus = async () => { try { setIsLoading(true); const response = await twoFactorService.getStatus(); setIsEnabled(response.data.enabled); } catch (err) { console.error('Failed to fetch 2FA status:', err); } finally { setIsLoading(false); } }; const handleSetup = async () => { try { setIsLoading(true); setError(null); const response = await twoFactorService.setup(); setSetupData(response.data); setStep('setup'); } catch (err: unknown) { const errorMessage = err instanceof Error ? err.message : 'Failed to setup 2FA'; setError(errorMessage); } finally { setIsLoading(false); } }; const handleVerify = async () => { if (verificationCode.length !== 6) { setError('Please enter a 6-digit verification code'); return; } try { setIsLoading(true); setError(null); const response = await twoFactorService.enable(verificationCode); setBackupCodes(response.data.backupCodes); setStep('backup-codes'); setIsEnabled(true); onStatusChange?.(true); } catch (err: unknown) { const errorMessage = err instanceof Error ? err.message : 'Invalid verification code'; setError(errorMessage); } finally { setIsLoading(false); } }; const handleDisable = async () => { if (!disablePassword) { setError('Please enter your password'); return; } try { setIsLoading(true); setError(null); await twoFactorService.disable(disablePassword); setIsEnabled(false); setStep('status'); setDisablePassword(''); setSuccess('Two-factor authentication has been disabled'); onStatusChange?.(false); } catch (err: unknown) { const errorMessage = err instanceof Error ? err.message : 'Failed to disable 2FA'; setError(errorMessage); } finally { setIsLoading(false); } }; const handleRegenerateBackupCodes = async () => { const password = prompt('Enter your password to regenerate backup codes:'); if (!password) return; try { setIsLoading(true); setError(null); const response = await twoFactorService.regenerateBackupCodes(password); setBackupCodes(response.data.backupCodes); setStep('backup-codes'); setSuccess('New backup codes generated successfully'); } catch (err: unknown) { const errorMessage = err instanceof Error ? err.message : 'Failed to regenerate backup codes'; setError(errorMessage); } finally { setIsLoading(false); } }; const copyBackupCodes = () => { const codesText = backupCodes.join('\n'); navigator.clipboard.writeText(codesText); setBackupCodesCopied(true); setTimeout(() => setBackupCodesCopied(false), 2000); }; const downloadBackupCodes = () => { const codesText = `Re-Quest 2FA Backup Codes\n${'='.repeat(30)}\n\nKeep these codes safe. Each code can only be used once.\n\n${backupCodes.map((code, i) => `${i + 1}. ${code}`).join('\n')}\n\nGenerated: ${new Date().toISOString()}`; const blob = new Blob([codesText], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 're-quest-backup-codes.txt'; a.click(); URL.revokeObjectURL(url); }; // Clear messages after timeout useEffect(() => { if (success) { const timer = setTimeout(() => setSuccess(null), 5000); return () => clearTimeout(timer); } }, [success]); if (isLoading && step === 'status') { return (
); } // Status view - show current 2FA status if (step === 'status') { return (
{error && (
{error}
)} {success && (
{success}
)}
Security

Two-Factor Authentication

Add an extra layer of security to your account using an authenticator app

{isEnabled ? 'Enabled' : 'Not Enabled'}
{isEnabled ? (
) : ( )}
); } // Setup view - show QR code if (step === 'setup' && setupData) { return (

Setup Two-Factor Authentication

Scan this QR code with your authenticator app (Google Authenticator, Authy, etc.)

{/* QR Code */}
2FA QR Code

Can't scan? Enter this code manually:

{setupData.manualEntry}
{/* Verification */}

Verify Setup

Enter the 6-digit code from your authenticator app to verify setup

{error && (
{error}
)}
{ const value = e.target.value.replace(/\D/g, '').slice(0, 6); setVerificationCode(value); setError(null); }} placeholder="000000" maxLength={6} className="w-32 h-[40px] px-4 border border-[#00293D]/20 rounded-[15px] text-[18px] font-mono text-center text-[#00293D] placeholder:text-[#00293D]/30 focus:outline-none focus:border-[#E58625] tracking-[0.3em]" />
); } // Backup codes view if (step === 'backup-codes') { return (

Two-Factor Authentication Enabled

Save these backup codes in a secure place. You can use them to access your account if you lose your phone.

{backupCodes.map((code, index) => (
{code}
))}

Each code can only be used once. Store them safely!

); } // Disable view if (step === 'disable') { return (

Disable Two-Factor Authentication

Enter your password to confirm you want to disable 2FA. This will make your account less secure.

{error && (
{error}
)}
{ setDisablePassword(e.target.value); setError(null); }} placeholder="Enter your password" className="w-full h-[40px] px-4 pr-12 border border-[#00293D]/20 rounded-[15px] text-[14px] font-serif text-[#00293D] placeholder:text-[#00293D]/40 focus:outline-none focus:border-[#E58625]" />
); } return null; }