import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.57.4'; const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', }; const supabaseUrl = Deno.env.get('SUPABASE_URL')!; const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!; Deno.serve(async (req) => { if (req.method === 'OPTIONS') { return new Response(null, { headers: corsHeaders }); } try { const { email, password } = await req.json(); if (!email || !password) { return new Response( JSON.stringify({ error: 'Email and password are required' }), { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ); } // Create admin client const supabaseAdmin = createClient(supabaseUrl, supabaseServiceKey, { auth: { autoRefreshToken: false, persistSession: false, }, }); // Verify credentials using signInWithPassword (doesn't create session with admin client) const { data: authData, error: authError } = await supabaseAdmin.auth.signInWithPassword({ email, password, }); if (authError || !authData.user) { console.error('Auth error:', authError); return new Response( JSON.stringify({ error: authError?.message || 'Invalid credentials' }), { status: 401, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ); } const userId = authData.user.id; // Check if user is banned const { data: profile, error: profileError } = await supabaseAdmin .from('profiles') .select('banned, ban_reason') .eq('user_id', userId) .single(); if (profileError) { console.error('Profile check error:', profileError); } if (profile?.banned) { return new Response( JSON.stringify({ error: 'Account suspended', banned: true, banReason: profile.ban_reason }), { status: 403, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ); } // Check for MFA enrollment const { data: factors, error: factorsError } = await supabaseAdmin.auth.admin.mfa.listFactors({ userId, }); if (factorsError) { console.error('MFA factors check error:', factorsError); // Continue - assume no MFA if check fails } const verifiedFactors = factors?.totp?.filter((f) => f.status === 'verified') || []; if (verifiedFactors.length > 0) { // User has MFA - create a challenge but don't create session const factorId = verifiedFactors[0].id; // Create MFA challenge const { data: challengeData, error: challengeError } = await supabaseAdmin.auth.mfa.challenge({ factorId, }); if (challengeError) { console.error('Challenge creation error:', challengeError); return new Response( JSON.stringify({ error: 'Failed to create MFA challenge' }), { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ); } return new Response( JSON.stringify({ mfaRequired: true, factorId, challengeId: challengeData.id, userId, // Needed for verification step }), { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ); } // No MFA required - return session return new Response( JSON.stringify({ mfaRequired: false, session: authData.session, user: authData.user, }), { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ); } catch (error) { console.error('Unexpected error:', error); return new Response( JSON.stringify({ error: 'Internal server error' }), { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ); } });