mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 14:11:13 -05:00
129 lines
3.9 KiB
TypeScript
129 lines
3.9 KiB
TypeScript
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' } }
|
|
);
|
|
}
|
|
});
|