'use client'; import { useState, useEffect } from 'react'; import { Dialog, DialogTitle, DialogContent, DialogActions, Button, TextField, Typography, Alert, CircularProgress, Box, Link as MuiLink, } from '@mui/material'; import { Security } from '@mui/icons-material'; import axios from 'axios'; const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3020'; interface MFAVerificationDialogProps { open: boolean; userId: string; mfaMethod: 'totp' | 'email'; onVerified: (tokens: { accessToken: string; refreshToken: string }, user: any) => void; onCancel: () => void; } export function MFAVerificationDialog({ open, userId, mfaMethod, onVerified, onCancel, }: MFAVerificationDialogProps) { const [verificationCode, setVerificationCode] = useState(''); const [error, setError] = useState(null); const [isVerifying, setIsVerifying] = useState(false); const [isSendingCode, setIsSendingCode] = useState(false); const [codeSent, setCodeSent] = useState(false); // Auto-send email code when dialog opens useEffect(() => { if (open && mfaMethod === 'email' && !codeSent) { sendEmailCode(); } }, [open, mfaMethod, codeSent]); const sendEmailCode = async () => { try { setIsSendingCode(true); setError(null); await axios.post(`${API_BASE_URL}/api/v1/auth/mfa/email/send-code`, { userId, }); setCodeSent(true); } catch (err: any) { console.error('Failed to send email code:', err); setError(err.response?.data?.message || 'Failed to send verification code'); } finally { setIsSendingCode(false); } }; const handleVerify = async () => { if (!verificationCode || verificationCode.length < 6) { setError('Please enter a valid verification code'); return; } try { setIsVerifying(true); setError(null); const response = await axios.post(`${API_BASE_URL}/api/v1/auth/mfa/verify`, { userId, code: verificationCode, }); if (response.data.success) { // Get tokens after successful MFA verification // Note: Backend should return tokens after MFA verification // For now, we'll assume success and let the parent handle it onVerified(response.data.tokens, response.data.user); } } catch (err: any) { console.error('Failed to verify MFA code:', err); setError(err.response?.data?.message || 'Invalid verification code'); } finally { setIsVerifying(false); } }; const handleResendCode = async () => { setCodeSent(false); setVerificationCode(''); setError(null); await sendEmailCode(); }; const handleCancel = () => { setVerificationCode(''); setError(null); setCodeSent(false); onCancel(); }; return ( {mfaMethod === 'totp' ? ( <> Enter the 6-digit code from your authenticator app to continue. ) : ( <> {codeSent ? 'A 6-digit verification code has been sent to your email.' : 'Sending verification code to your email...'} {isSendingCode && ( )} )} {error && ( {error} )} setVerificationCode(e.target.value.replace(/\D/g, '').slice(0, mfaMethod === 'totp' ? 6 : 6)) } disabled={isVerifying || isSendingCode} autoFocus inputProps={{ 'aria-label': 'Six digit verification code', style: { textAlign: 'center', fontSize: '1.5rem', letterSpacing: '0.5rem' }, maxLength: 6, }} /> {mfaMethod === 'email' && codeSent && ( Didn't receive the code?{' '} Resend )} Tip: You can also use a backup code if you don't have access to your{' '} {mfaMethod === 'totp' ? 'authenticator app' : 'email'}. ); }