Reverted to commit 0091584677

This commit is contained in:
gpt-engineer-app[bot]
2025-11-01 15:22:30 +00:00
parent 26e5753807
commit 133141d474
125 changed files with 2316 additions and 9102 deletions

View File

@@ -8,9 +8,8 @@ import { Label } from '@/components/ui/label';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Separator } from '@/components/ui/separator';
import { Zap, Mail, Lock, User, AlertCircle, Eye, EyeOff, Shield } from 'lucide-react';
import { Zap, Mail, Lock, User, AlertCircle, Eye, EyeOff } from 'lucide-react';
import { supabase } from '@/integrations/supabase/client';
import { useToast } from '@/hooks/use-toast';
import { getErrorMessage } from '@/lib/errorHandler';
@@ -37,9 +36,6 @@ export default function Auth() {
const [signInCaptchaToken, setSignInCaptchaToken] = useState<string | null>(null);
const [signInCaptchaKey, setSignInCaptchaKey] = useState(0);
const [mfaFactorId, setMfaFactorId] = useState<string | null>(null);
const [mfaPendingEmail, setMfaPendingEmail] = useState<string | null>(null);
const [mfaChallengeId, setMfaChallengeId] = useState<string | null>(null);
const [mfaPendingUserId, setMfaPendingUserId] = useState<string | null>(null);
const emailParam = searchParams.get('email');
const messageParam = searchParams.get('message');
@@ -95,65 +91,89 @@ export default function Auth() {
setSignInCaptchaToken(null);
try {
// Call server-side auth check with MFA detection
const { data: authResult, error: authError } = await supabase.functions.invoke(
'auth-with-mfa-check',
{
body: {
email: formData.email,
password: formData.password,
captchaToken: tokenToUse,
},
const {
data,
error
} = await supabase.auth.signInWithPassword({
email: formData.email,
password: formData.password,
options: {
captchaToken: tokenToUse
}
);
});
if (error) throw error;
if (authError || authResult.error) {
throw new Error(authResult?.error || authError?.message || 'Authentication failed');
}
// Check if user is banned
if (authResult.banned) {
const reason = authResult.banReason
? `Reason: ${authResult.banReason}`
// CRITICAL: Check ban status immediately after successful authentication
const { data: profile } = await supabase
.from('profiles')
.select('banned, ban_reason')
.eq('user_id', data.user.id)
.single();
if (profile?.banned) {
// Sign out immediately
await supabase.auth.signOut();
const reason = profile.ban_reason
? `Reason: ${profile.ban_reason}`
: 'Contact support for assistance.';
toast({
variant: "destructive",
title: "Account Suspended",
description: `Your account has been suspended. ${reason}`,
duration: 10000,
duration: 10000
});
setLoading(false);
return;
return; // Stop authentication flow
}
// Check if MFA is required
if (authResult.mfaRequired) {
// NO SESSION EXISTS YET - show MFA challenge
console.log('[Auth] MFA required - no session created yet');
setMfaFactorId(authResult.factorId);
setMfaChallengeId(authResult.challengeId);
setMfaPendingUserId(authResult.userId);
setLoading(false);
return; // User has NO session - MFA modal will show
// Check if MFA is required (user exists but no session)
if (data.user && !data.session) {
const totpFactor = data.user.factors?.find(f => f.factor_type === 'totp' && f.status === 'verified');
if (totpFactor) {
setMfaFactorId(totpFactor.id);
setLoading(false);
return;
}
}
// No MFA required - user has session
console.log('[Auth] No MFA required - user authenticated');
// Set the session in Supabase client
if (authResult.session) {
await supabase.auth.setSession(authResult.session);
// Track auth method for audit logging
setAuthMethod('password');
// Check if MFA step-up is required
const { handlePostAuthFlow } = await import('@/lib/authService');
const postAuthResult = await handlePostAuthFlow(data.session, 'password');
if (postAuthResult.success && postAuthResult.data.shouldRedirect) {
// Get the TOTP factor ID
const { data: factors } = await supabase.auth.mfa.listFactors();
const totpFactor = factors?.totp?.find(f => f.status === 'verified');
if (totpFactor) {
setMfaFactorId(totpFactor.id);
setLoading(false);
return; // Stay on page, show MFA modal
}
}
toast({
title: "Welcome back!",
description: "You've been signed in successfully."
});
// Navigate after brief delay
setTimeout(() => {
navigate('/');
// Verify session was stored
setTimeout(async () => {
const { data: { session } } = await supabase.auth.getSession();
if (!session) {
toast({
variant: "destructive",
title: "Session Error",
description: "Login succeeded but session was not stored. Please check your browser settings and enable cookies/storage."
});
} else {
toast({
title: "Welcome back!",
description: "You've been signed in successfully."
});
}
}, 500);
} catch (error) {
@@ -165,11 +185,11 @@ export default function Auth() {
// Enhanced error messages
const errorMsg = getErrorMessage(error);
let errorMessage = errorMsg;
if (errorMsg.includes('Invalid login credentials') || errorMsg.includes('Invalid credentials')) {
if (errorMsg.includes('Invalid login credentials')) {
errorMessage = 'Invalid email or password. Please try again.';
} else if (errorMsg.includes('Email not confirmed')) {
errorMessage = 'Please confirm your email address before signing in.';
} else if (errorMsg.includes('Too many requests')) {
} else if (error.message.includes('Too many requests')) {
errorMessage = 'Too many login attempts. Please wait a few minutes and try again.';
}
@@ -184,40 +204,33 @@ export default function Auth() {
};
const handleMfaSuccess = async () => {
console.log('[Auth] MFA verification succeeded - no further action needed');
// Verify AAL upgrade was successful
const { data: { session } } = await supabase.auth.getSession();
const verification = await verifyMfaUpgrade(session);
// MFA verification is handled by MFAChallenge component
// which calls verify-mfa-and-login edge function
// The session is automatically set by the edge function
if (!verification.success) {
toast({
variant: "destructive",
title: "MFA Verification Failed",
description: verification.error || "Failed to upgrade session. Please try again."
});
// Force sign out on verification failure
await supabase.auth.signOut();
setMfaFactorId(null);
return;
}
// Clear state
setMfaFactorId(null);
setMfaChallengeId(null);
setMfaPendingUserId(null);
toast({
title: "Authentication complete",
description: "You've been signed in successfully.",
title: "Welcome back!",
description: "You've been signed in successfully."
});
setTimeout(() => {
navigate('/');
}, 500);
};
const handleMfaCancel = async () => {
console.log('[Auth] User cancelled MFA verification');
// Clear state - no credentials stored
const handleMfaCancel = () => {
setMfaFactorId(null);
setMfaChallengeId(null);
setMfaPendingUserId(null);
setSignInCaptchaKey(prev => prev + 1);
toast({
title: "Authentication cancelled",
description: "Please sign in again when you're ready to complete two-factor authentication.",
});
};
const handleSignUp = async (e: React.FormEvent) => {
e.preventDefault();
@@ -268,7 +281,6 @@ export default function Auth() {
email: formData.email,
password: formData.password,
options: {
emailRedirectTo: `${window.location.origin}/auth/callback`,
captchaToken: tokenToUse,
data: {
username: formData.username,
@@ -415,35 +427,11 @@ export default function Auth() {
)}
{mfaFactorId ? (
<Dialog open={!!mfaFactorId} onOpenChange={(isOpen) => {
if (!isOpen) {
handleMfaCancel();
}
}}>
<DialogContent
className="sm:max-w-md"
onInteractOutside={(e) => e.preventDefault()}
onEscapeKeyDown={(e) => e.preventDefault()}
>
<DialogHeader>
<div className="flex items-center gap-2 justify-center mb-2">
<Shield className="h-6 w-6 text-primary" />
<DialogTitle>Two-Factor Authentication Required</DialogTitle>
</div>
<DialogDescription className="text-center">
Your account security settings require MFA verification to continue.
</DialogDescription>
</DialogHeader>
<MFAChallenge
factorId={mfaFactorId}
challengeId={mfaChallengeId}
userId={mfaPendingUserId}
onSuccess={handleMfaSuccess}
onCancel={handleMfaCancel}
/>
</DialogContent>
</Dialog>
<MFAChallenge
factorId={mfaFactorId}
onSuccess={handleMfaSuccess}
onCancel={handleMfaCancel}
/>
) : (
<>
<form onSubmit={handleSignIn} className="space-y-4">