import { useState, useEffect } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { Badge } from '@/components/ui/badge'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from '@/components/ui/alert-dialog'; import { useToast } from '@/hooks/use-toast'; import { useAuth } from '@/hooks/useAuth'; import { supabase } from '@/integrations/supabase/client'; import { Smartphone, Shield, Copy, Eye, EyeOff, Trash2, AlertCircle } from 'lucide-react'; interface TOTPFactor { id: string; friendly_name?: string; factor_type: string; status: string; created_at: string; } export function TOTPSetup() { const { user } = useAuth(); const { toast } = useToast(); const [factors, setFactors] = useState([]); const [loading, setLoading] = useState(false); const [enrolling, setEnrolling] = useState(false); const [qrCode, setQrCode] = useState(''); const [secret, setSecret] = useState(''); const [factorId, setFactorId] = useState(''); const [verificationCode, setVerificationCode] = useState(''); const [showSecret, setShowSecret] = useState(false); useEffect(() => { fetchTOTPFactors(); }, [user]); const fetchTOTPFactors = async () => { if (!user) return; try { const { data, error } = await supabase.auth.mfa.listFactors(); if (error) throw error; const totpFactors = (data.totp || []).map(factor => ({ id: factor.id, friendly_name: factor.friendly_name || 'Authenticator App', factor_type: factor.factor_type || 'totp', status: factor.status, created_at: factor.created_at })); setFactors(totpFactors); } catch (error: any) { console.error('Error fetching TOTP factors:', error); } }; const startEnrollment = async () => { if (!user) return; setLoading(true); try { const { data, error } = await supabase.auth.mfa.enroll({ factorType: 'totp', friendlyName: 'Authenticator App' }); if (error) throw error; setQrCode(data.totp.qr_code); setSecret(data.totp.secret); setFactorId(data.id); setEnrolling(true); } catch (error: any) { toast({ title: 'Error', description: error.message || 'Failed to start TOTP enrollment', variant: 'destructive' }); } finally { setLoading(false); } }; const verifyAndEnable = async () => { if (!factorId || !verificationCode.trim()) { toast({ title: 'Error', description: 'Please enter the verification code', variant: 'destructive' }); return; } setLoading(true); try { // Step 1: Create a challenge first const { data: challengeData, error: challengeError } = await supabase.auth.mfa.challenge({ factorId }); if (challengeError) throw challengeError; // Step 2: Verify using the challengeId from the challenge response const { error: verifyError } = await supabase.auth.mfa.verify({ factorId, challengeId: challengeData.id, code: verificationCode.trim() }); if (verifyError) throw verifyError; toast({ title: 'TOTP Enabled', description: 'Two-factor authentication has been successfully enabled for your account.' }); // Reset state and refresh factors setEnrolling(false); setQrCode(''); setSecret(''); setFactorId(''); setVerificationCode(''); fetchTOTPFactors(); } catch (error: any) { toast({ title: 'Error', description: error.message || 'Invalid verification code. Please try again.', variant: 'destructive' }); } finally { setLoading(false); } }; const unenrollFactor = async (factorId: string) => { setLoading(true); try { const { error } = await supabase.auth.mfa.unenroll({ factorId }); if (error) throw error; toast({ title: 'TOTP Disabled', description: 'Two-factor authentication has been disabled for your account.' }); fetchTOTPFactors(); } catch (error: any) { toast({ title: 'Error', description: error.message || 'Failed to disable TOTP', variant: 'destructive' }); } finally { setLoading(false); } }; const copySecret = () => { navigator.clipboard.writeText(secret); toast({ title: 'Copied', description: 'Secret key copied to clipboard' }); }; const cancelEnrollment = () => { setEnrolling(false); setQrCode(''); setSecret(''); setFactorId(''); setVerificationCode(''); }; const activeFactor = factors.find(f => f.status === 'verified'); if (enrolling) { return ( Set Up Authenticator App Scan the QR code with your authenticator app, then enter the verification code below. {/* QR Code */}
TOTP QR Code
{/* Manual Entry */}
{/* Verification */}
setVerificationCode(e.target.value)} placeholder="000000" maxLength={6} className="text-center text-lg tracking-widest font-mono" />
); } return ( Add an extra layer of security to your account with two-factor authentication. {activeFactor ? (
Two-factor authentication is enabled for your account. You'll be prompted for a verification code when signing in.

{activeFactor.friendly_name || 'Authenticator App'}

Enabled {new Date(activeFactor.created_at).toLocaleDateString()}

Active Disable Two-Factor Authentication Are you sure you want to disable two-factor authentication? This will make your account less secure. Cancel unenrollFactor(activeFactor.id)} className="bg-destructive hover:bg-destructive/90" > Disable TOTP
) : (

Authenticator App

Use an authenticator app to generate verification codes

)}
); }