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 { useToast } from '@/hooks/use-toast'; import { useAuth } from '@/hooks/useAuth'; import { supabase } from '@/integrations/supabase/client'; import { Smartphone, Shield, Copy, Eye, EyeOff, Trash2 } from 'lucide-react'; import { MFARemovalDialog } from './MFARemovalDialog'; 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); const [showRemovalDialog, setShowRemovalDialog] = 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; // Check if user signed in via OAuth const { data: { session } } = await supabase.auth.getSession(); const provider = session?.user?.app_metadata?.provider; const isOAuthUser = provider === 'google' || provider === 'discord'; toast({ title: 'TOTP Enabled', description: isOAuthUser ? 'Please verify with your authenticator code to continue.' : 'Please sign in again to activate MFA protection.' }); if (isOAuthUser) { // For OAuth users, trigger step-up flow immediately setTimeout(() => { sessionStorage.setItem('mfa_step_up_required', 'true'); window.location.href = '/auth/mfa-step-up'; }, 1500); } else { // For email/password users, force sign out to require MFA on next login setTimeout(async () => { await supabase.auth.signOut(); window.location.href = '/auth'; }, 2000); } } catch (error: any) { toast({ title: 'Error', description: error.message || 'Invalid verification code. Please try again.', variant: 'destructive' }); } finally { setLoading(false); } }; const handleRemovalSuccess = async () => { await fetchTOTPFactors(); }; 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
) : (

Authenticator App

Use an authenticator app to generate verification codes

)}
); }