mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 00:31:13 -05:00
Refactor code structure and remove redundant changes
This commit is contained in:
295
src-old/components/auth/MFARemovalDialog.tsx
Normal file
295
src-old/components/auth/MFARemovalDialog.tsx
Normal file
@@ -0,0 +1,295 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { supabase } from '@/lib/supabaseClient';
|
||||
import { invokeWithTracking } from '@/lib/edgeFunctionTracking';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { getErrorMessage } from '@/lib/errorHandler';
|
||||
import { useRequireMFA } from '@/hooks/useRequireMFA';
|
||||
import { getSessionAAL } from '@/types/supabase-session';
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from '@/components/ui/alert-dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||
import { Shield, AlertTriangle } from 'lucide-react';
|
||||
|
||||
interface MFARemovalDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
factorId: string;
|
||||
onSuccess: () => void;
|
||||
}
|
||||
|
||||
export function MFARemovalDialog({ open, onOpenChange, factorId, onSuccess }: MFARemovalDialogProps) {
|
||||
const { requiresMFA } = useRequireMFA();
|
||||
const { toast } = useToast();
|
||||
const [step, setStep] = useState<'password' | 'totp' | 'confirm'>('password');
|
||||
const [password, setPassword] = useState('');
|
||||
const [totpCode, setTotpCode] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// Phase 1: Check AAL2 requirement on dialog open
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
const checkAalLevel = async (): Promise<void> => {
|
||||
const { data: { session } } = await supabase.auth.getSession();
|
||||
const currentAal = getSessionAAL(session);
|
||||
|
||||
if (currentAal !== 'aal2') {
|
||||
toast({
|
||||
title: 'Multi-Factor Authentication Required',
|
||||
description: 'Please verify your identity with Multi-Factor Authentication before making security changes',
|
||||
variant: 'destructive'
|
||||
});
|
||||
onOpenChange(false);
|
||||
}
|
||||
};
|
||||
|
||||
checkAalLevel();
|
||||
}
|
||||
}, [open, onOpenChange]);
|
||||
|
||||
const handleClose = () => {
|
||||
setStep('password');
|
||||
setPassword('');
|
||||
setTotpCode('');
|
||||
setLoading(false);
|
||||
onOpenChange(false);
|
||||
};
|
||||
|
||||
const handlePasswordVerification = async () => {
|
||||
if (!password.trim()) {
|
||||
toast({
|
||||
title: 'Password Required',
|
||||
description: 'Please enter your password',
|
||||
variant: 'destructive'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
// Get current user email
|
||||
const { data: { user } } = await supabase.auth.getUser();
|
||||
if (!user?.email) throw new Error('User email not found');
|
||||
|
||||
// Re-authenticate with password
|
||||
const { error } = await supabase.auth.signInWithPassword({
|
||||
email: user.email,
|
||||
password: password
|
||||
});
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
toast({
|
||||
title: 'Password Verified',
|
||||
description: 'Password verified successfully'
|
||||
});
|
||||
setStep('totp');
|
||||
} catch (error: unknown) {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: getErrorMessage(error),
|
||||
variant: 'destructive'
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTOTPVerification = async () => {
|
||||
if (!totpCode.trim() || totpCode.length !== 6) {
|
||||
toast({
|
||||
title: 'Invalid Code',
|
||||
description: 'Please enter a valid 6-digit code',
|
||||
variant: 'destructive'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
// Create challenge
|
||||
const { data: challengeData, error: challengeError } = await supabase.auth.mfa.challenge({
|
||||
factorId
|
||||
});
|
||||
|
||||
if (challengeError) throw challengeError;
|
||||
|
||||
// Verify TOTP code
|
||||
const { error: verifyError } = await supabase.auth.mfa.verify({
|
||||
factorId,
|
||||
challengeId: challengeData.id,
|
||||
code: totpCode.trim()
|
||||
});
|
||||
|
||||
if (verifyError) throw verifyError;
|
||||
|
||||
// Phase 1: Verify session is at AAL2 after TOTP verification
|
||||
const { data: { session } } = await supabase.auth.getSession();
|
||||
const currentAal = getSessionAAL(session);
|
||||
|
||||
if (currentAal !== 'aal2') {
|
||||
throw new Error('Session must be at AAL2 to remove MFA');
|
||||
}
|
||||
|
||||
toast({
|
||||
title: 'Code Verified',
|
||||
description: 'TOTP code verified successfully'
|
||||
});
|
||||
setStep('confirm');
|
||||
} catch (error: unknown) {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: getErrorMessage(error),
|
||||
variant: 'destructive'
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMFARemoval = async () => {
|
||||
// Phase 2: Check if user's role requires MFA
|
||||
if (requiresMFA) {
|
||||
toast({
|
||||
title: 'Multi-Factor Authentication Required',
|
||||
description: 'Your role requires Multi-Factor Authentication and it cannot be disabled',
|
||||
variant: 'destructive'
|
||||
});
|
||||
handleClose();
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
// Phase 3: Call edge function instead of direct unenroll
|
||||
const { data, error, requestId } = await invokeWithTracking(
|
||||
'mfa-unenroll',
|
||||
{ factorId },
|
||||
(await supabase.auth.getUser()).data.user?.id
|
||||
);
|
||||
|
||||
if (error) throw error;
|
||||
if (data?.error) throw new Error(data.error);
|
||||
|
||||
toast({
|
||||
title: 'Multi-Factor Authentication Disabled',
|
||||
description: 'Multi-Factor Authentication has been disabled'
|
||||
});
|
||||
handleClose();
|
||||
onSuccess();
|
||||
} catch (error: unknown) {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: getErrorMessage(error),
|
||||
variant: 'destructive'
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<AlertDialog open={open} onOpenChange={onOpenChange}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle className="flex items-center gap-2">
|
||||
<Shield className="h-5 w-5 text-destructive" />
|
||||
Disable Multi-Factor Authentication
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription asChild>
|
||||
<div className="space-y-4">
|
||||
<Alert variant="destructive">
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
<AlertDescription>
|
||||
Disabling Multi-Factor Authentication will make your account less secure. You'll need to verify your identity first.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
{step === 'password' && (
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm">Step 1 of 3: Enter your password to continue</p>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handlePasswordVerification()}
|
||||
placeholder="Enter your password"
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{step === 'totp' && (
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm">Step 2 of 3: Enter your current code</p>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="totp">Code from Authenticator App</Label>
|
||||
<Input
|
||||
id="totp"
|
||||
type="text"
|
||||
value={totpCode}
|
||||
onChange={(e) => setTotpCode(e.target.value.replace(/\D/g, '').slice(0, 6))}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleTOTPVerification()}
|
||||
placeholder="000000"
|
||||
maxLength={6}
|
||||
disabled={loading}
|
||||
className="text-center text-2xl tracking-widest"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{step === 'confirm' && (
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm font-semibold">Step 3 of 3: Final confirmation</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Are you sure you want to disable Multi-Factor Authentication? This will:
|
||||
</p>
|
||||
<ul className="text-sm text-muted-foreground list-disc list-inside space-y-1">
|
||||
<li>Remove Multi-Factor Authentication protection from your account</li>
|
||||
<li>Send a security notification email</li>
|
||||
<li>Log this action in your security history</li>
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel onClick={handleClose} disabled={loading}>
|
||||
Cancel
|
||||
</AlertDialogCancel>
|
||||
{step === 'password' && (
|
||||
<Button onClick={handlePasswordVerification} disabled={loading}>
|
||||
{loading ? 'Verifying...' : 'Continue'}
|
||||
</Button>
|
||||
)}
|
||||
{step === 'totp' && (
|
||||
<Button onClick={handleTOTPVerification} disabled={loading}>
|
||||
{loading ? 'Verifying...' : 'Continue'}
|
||||
</Button>
|
||||
)}
|
||||
{step === 'confirm' && (
|
||||
<AlertDialogAction onClick={handleMFARemoval} disabled={loading} className="bg-destructive text-destructive-foreground hover:bg-destructive/90">
|
||||
{loading ? 'Disabling...' : 'Disable Multi-Factor Authentication'}
|
||||
</AlertDialogAction>
|
||||
)}
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user