Improve security by verifying user authentication and authorization

Update the 'process-selective-approval' Supabase function to enforce authentication and authorization checks before processing requests. Also, modify the 'upload-image' function to prevent banned users from uploading images. Additionally, enable future React Router v7 features for enhanced navigation.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 6d6e48da-5b1b-47f9-a65c-9fa4a352936a
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7cdf4e95-3f41-4180-b8e3-8ef56d032c0e/6d6e48da-5b1b-47f9-a65c-9fa4a352936a/u05utRo
This commit is contained in:
pac7
2025-10-07 20:12:39 +00:00
parent ff4a1521bb
commit b8787ee6de
6 changed files with 120 additions and 24 deletions

View File

@@ -8,7 +8,6 @@ const corsHeaders = {
interface ApprovalRequest {
itemIds: string[];
userId: string;
submissionId: string;
}
@@ -49,12 +48,63 @@ serve(async (req) => {
}
try {
// Verify authentication first with a client that respects RLS
const authHeader = req.headers.get('Authorization');
if (!authHeader) {
return new Response(
JSON.stringify({ error: 'Authentication required. Please log in.' }),
{ status: 401, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}
// Create Supabase client with user's auth token to verify authentication
const supabaseUrl = Deno.env.get('SUPABASE_URL') ?? '';
const supabaseAnonKey = Deno.env.get('SUPABASE_ANON_KEY') ?? '';
const supabaseAuth = createClient(supabaseUrl, supabaseAnonKey, {
global: { headers: { Authorization: authHeader } }
});
// Verify JWT and get authenticated user
const { data: { user }, error: authError } = await supabaseAuth.auth.getUser();
if (authError || !user) {
console.error('Auth verification failed:', authError);
return new Response(
JSON.stringify({ error: 'Invalid authentication token.' }),
{ status: 401, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}
const authenticatedUserId = user.id;
// Create service role client for privileged operations (including role check)
const supabase = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
);
const { itemIds, userId, submissionId }: ApprovalRequest = await req.json();
// Check if user has moderator permissions using service role to bypass RLS
const { data: profile, error: profileError } = await supabase
.from('profiles')
.select('role')
.eq('user_id', authenticatedUserId)
.single();
if (profileError || !profile) {
console.error('Failed to fetch profile:', profileError);
return new Response(
JSON.stringify({ error: 'User profile not found.' }),
{ status: 403, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}
if (profile.role !== 'moderator' && profile.role !== 'admin') {
return new Response(
JSON.stringify({ error: 'Insufficient permissions. Moderator role required.' }),
{ status: 403, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}
const { itemIds, submissionId }: ApprovalRequest = await req.json();
// UUID validation regex
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -74,21 +124,6 @@ serve(async (req) => {
);
}
// Validate userId
if (!userId || typeof userId !== 'string' || userId.trim() === '') {
return new Response(
JSON.stringify({ error: 'userId is required and must be a non-empty string' }),
{ status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}
if (!uuidRegex.test(userId)) {
return new Response(
JSON.stringify({ error: 'userId must be a valid UUID format' }),
{ status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}
// Validate submissionId
if (!submissionId || typeof submissionId !== 'string' || submissionId.trim() === '') {
return new Response(
@@ -104,7 +139,7 @@ serve(async (req) => {
);
}
console.log('Processing selective approval:', { itemIds, userId, submissionId });
console.log('Processing selective approval:', { itemIds, userId: authenticatedUserId, submissionId });
// Fetch all items for the submission
const { data: items, error: fetchError } = await supabase
@@ -241,7 +276,7 @@ serve(async (req) => {
.from('content_submissions')
.update({
status: allApproved ? 'approved' : 'partially_approved',
reviewer_id: userId,
reviewer_id: authenticatedUserId,
reviewed_at: new Date().toISOString()
})
.eq('id', submissionId);