Files
thrilltrack-explorer/supabase/functions/cancel-email-change/index.ts
pac7 32a83013e5 Update email cancellation and location detection functions
Refactor Supabase function to use explicit JWT verification via auth.getUser, and enhance the location detection function with configurable geolocation API endpoints.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 364fb426-1d27-49b2-a244-a34e41c335e4
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
2025-10-08 12:32:19 +00:00

111 lines
3.6 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',
};
Deno.serve(async (req) => {
// Handle CORS preflight requests
if (req.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
try {
// Get the user from the authorization header
const authHeader = req.headers.get('Authorization');
if (!authHeader) {
console.error('Missing authorization header');
throw new Error('No authorization header provided. Please ensure you are logged in.');
}
// Extract the JWT token from the Authorization header
const token = authHeader.replace('Bearer ', '');
// Create admin client with service role key (no user token in global headers)
// This ensures all DB operations run with full admin privileges
const supabaseUrl = Deno.env.get('SUPABASE_URL');
const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY');
if (!supabaseUrl || !supabaseServiceKey) {
throw new Error('Missing Supabase configuration');
}
const supabaseAdmin = createClient(supabaseUrl, supabaseServiceKey, {
auth: {
autoRefreshToken: false,
persistSession: false
}
});
// Verify the user's JWT token by passing it explicitly to getUser()
// Note: verify_jwt = true in config.toml means Supabase has already validated the JWT
const { data: { user }, error: authError } = await supabaseAdmin.auth.getUser(token);
if (authError || !user) {
console.error('Auth verification failed:', authError);
throw new Error('Invalid session token. Please refresh the page and try again.');
}
const userId = user.id;
console.log(`Cancelling email change for user ${userId}`);
// Call the database function to clear email change fields
// This function has SECURITY DEFINER privileges to access auth.users
const { data: cancelled, error: cancelError } = await supabaseAdmin
.rpc('cancel_user_email_change', { _user_id: userId });
if (cancelError || !cancelled) {
console.error('Error cancelling email change:', cancelError);
throw new Error('Unable to cancel email change: ' + (cancelError?.message || 'Unknown error'));
}
console.log(`Successfully cancelled email change for user ${userId}`);
// Log the cancellation in admin_audit_log
const { error: auditError } = await supabaseAdmin
.from('admin_audit_log')
.insert({
admin_user_id: userId,
target_user_id: userId,
action: 'email_change_cancelled',
details: {
cancelled_at: new Date().toISOString(),
},
});
if (auditError) {
console.error('Error logging audit:', auditError);
// Don't fail the request if audit logging fails
}
return new Response(
JSON.stringify({
success: true,
message: 'Email change cancelled successfully',
user: {
id: userId,
email: null,
new_email: null,
},
}),
{
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: 200,
}
);
} catch (error) {
console.error('Error in cancel-email-change function:', error);
return new Response(
JSON.stringify({
success: false,
error: error instanceof Error ? error.message : 'An unknown error occurred',
}),
{
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: 400,
}
);
}
});