mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 14:11:13 -05:00
92 lines
2.9 KiB
TypeScript
92 lines
2.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 { challengeId, factorId, code, userId } = await req.json();
|
|
|
|
if (!challengeId || !factorId || !code || !userId) {
|
|
return new Response(
|
|
JSON.stringify({ error: 'Missing required fields' }),
|
|
{ status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
);
|
|
}
|
|
|
|
// Create admin client
|
|
const supabaseAdmin = createClient(supabaseUrl, supabaseServiceKey, {
|
|
auth: {
|
|
autoRefreshToken: false,
|
|
persistSession: false,
|
|
},
|
|
});
|
|
|
|
// Verify TOTP code
|
|
const { data: verifyData, error: verifyError } = await supabaseAdmin.auth.mfa.verify({
|
|
factorId,
|
|
challengeId,
|
|
code,
|
|
});
|
|
|
|
if (verifyError || !verifyData) {
|
|
console.error('MFA verification error:', verifyError);
|
|
return new Response(
|
|
JSON.stringify({ error: verifyError?.message || 'Invalid verification code' }),
|
|
{ status: 401, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
);
|
|
}
|
|
|
|
// Verification successful - create AAL2 session using admin API
|
|
const { data: sessionData, error: sessionError } = await supabaseAdmin.auth.admin.createSession({
|
|
userId,
|
|
// This creates a session with AAL2
|
|
});
|
|
|
|
if (sessionError || !sessionData) {
|
|
console.error('Session creation error:', sessionError);
|
|
return new Response(
|
|
JSON.stringify({ error: 'Failed to create session' }),
|
|
{ status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
|
);
|
|
}
|
|
|
|
// Log successful MFA authentication
|
|
try {
|
|
await supabaseAdmin.rpc('log_admin_action', {
|
|
_admin_user_id: userId,
|
|
_target_user_id: userId,
|
|
_action: 'mfa_login_success',
|
|
_details: { timestamp: new Date().toISOString(), aal: 'aal2' },
|
|
});
|
|
} catch (logError) {
|
|
console.error('Audit log error:', logError);
|
|
// Don't fail the login if audit logging fails
|
|
}
|
|
|
|
return new Response(
|
|
JSON.stringify({
|
|
success: true,
|
|
session: sessionData.session,
|
|
user: sessionData.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' } }
|
|
);
|
|
}
|
|
});
|