Files
thrilltrack-explorer/supabase/functions/cancel-email-change/index.ts
pac7 6737431379 Improve error handling and navigation safety across the application
Add robust error handling for image uploads, improve navigation logic in AutocompleteSearch with toast notifications for missing identifiers, refine useIsMobile hook return type, and update Supabase function error reporting to handle non-Error types.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: a759d451-40bf-440d-96f5-a19ad6af18a8
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
2025-10-07 15:25:37 +00:00

119 lines
3.5 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',
};
// Helper function to decode JWT and extract user ID
function decodeJWT(token: string): { sub: string } | null {
try {
const parts = token.split('.');
if (parts.length !== 3) return null;
const payload = JSON.parse(atob(parts[1]));
return payload;
} catch (error) {
console.error('JWT decode error:', error);
return null;
}
}
Deno.serve(async (req) => {
// Handle CORS preflight requests
if (req.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
try {
// Create admin client with service role key
const supabaseAdmin = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? '',
{
auth: {
autoRefreshToken: false,
persistSession: false
}
}
);
// 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.');
}
const token = authHeader.replace('Bearer ', '');
console.log('Extracting user ID from JWT token...');
// Parse JWT token to get user ID directly
const payload = decodeJWT(token);
if (!payload || !payload.sub) {
console.error('Invalid JWT token structure');
throw new Error('Invalid session token. Please refresh the page and try again.');
}
const userId = payload.sub;
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,
}
);
}
});