mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 12:31:26 -05:00
Fix: Implement emergency MFA security fix
This commit is contained in:
@@ -37,6 +37,7 @@ export default function Auth() {
|
|||||||
const [signInCaptchaToken, setSignInCaptchaToken] = useState<string | null>(null);
|
const [signInCaptchaToken, setSignInCaptchaToken] = useState<string | null>(null);
|
||||||
const [signInCaptchaKey, setSignInCaptchaKey] = useState(0);
|
const [signInCaptchaKey, setSignInCaptchaKey] = useState(0);
|
||||||
const [mfaFactorId, setMfaFactorId] = useState<string | null>(null);
|
const [mfaFactorId, setMfaFactorId] = useState<string | null>(null);
|
||||||
|
const [mfaPendingEmail, setMfaPendingEmail] = useState<string | null>(null);
|
||||||
|
|
||||||
const emailParam = searchParams.get('email');
|
const emailParam = searchParams.get('email');
|
||||||
const messageParam = searchParams.get('message');
|
const messageParam = searchParams.get('message');
|
||||||
@@ -154,10 +155,17 @@ export default function Auth() {
|
|||||||
const totpFactor = factors?.totp?.find(f => f.status === 'verified');
|
const totpFactor = factors?.totp?.find(f => f.status === 'verified');
|
||||||
|
|
||||||
if (totpFactor) {
|
if (totpFactor) {
|
||||||
// Show MFA challenge
|
// CRITICAL SECURITY FIX: IMMEDIATELY DESTROY THE AAL1 SESSION
|
||||||
|
// The user MUST NOT have any active session before completing MFA
|
||||||
|
console.log('[Auth] MFA required - destroying AAL1 session before challenge');
|
||||||
|
await supabase.auth.signOut();
|
||||||
|
|
||||||
|
// Store email and factor ID in component state ONLY
|
||||||
|
// At this point, user has NO authenticated session
|
||||||
|
setMfaPendingEmail(formData.email);
|
||||||
setMfaFactorId(totpFactor.id);
|
setMfaFactorId(totpFactor.id);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
return; // Stay on page, show MFA modal
|
return; // User has NO session - MFA modal will show
|
||||||
} else {
|
} else {
|
||||||
// MFA is required but no factor found - FORCE SIGN OUT for security
|
// MFA is required but no factor found - FORCE SIGN OUT for security
|
||||||
console.error('[Auth] SECURITY: MFA required but no verified factor found');
|
console.error('[Auth] SECURITY: MFA required but no verified factor found');
|
||||||
@@ -245,35 +253,9 @@ export default function Auth() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleMfaCancel = async () => {
|
const handleMfaCancel = async () => {
|
||||||
try {
|
// Clear state variables
|
||||||
// CRITICAL SECURITY: Log cancellation attempt
|
|
||||||
const { data: { session } } = await supabase.auth.getSession();
|
|
||||||
if (session) {
|
|
||||||
try {
|
|
||||||
const { data: aalData } = await supabase.auth.mfa.getAuthenticatorAssuranceLevel();
|
|
||||||
await supabase.rpc('log_admin_action', {
|
|
||||||
_admin_user_id: session.user.id,
|
|
||||||
_action: 'mfa_verification_cancelled',
|
|
||||||
_target_user_id: session.user.id,
|
|
||||||
_details: {
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
reason: 'user_cancelled_mfa_prompt',
|
|
||||||
aal_before_cancel: aalData?.currentLevel || 'aal1'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (logError) {
|
|
||||||
console.error('Failed to log MFA cancellation:', logError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error during MFA cancellation:', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// CRITICAL SECURITY: User cannot bypass MFA if enrolled
|
|
||||||
// Cancelling MFA prompt MUST sign the user out
|
|
||||||
await supabase.auth.signOut();
|
|
||||||
|
|
||||||
setMfaFactorId(null);
|
setMfaFactorId(null);
|
||||||
|
setMfaPendingEmail(null);
|
||||||
setSignInCaptchaKey(prev => prev + 1);
|
setSignInCaptchaKey(prev => prev + 1);
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
Reference in New Issue
Block a user