Security: Enable RLS support by passing user JWT in Supabase requests

Replaced hardcoded anon key as Bearer token with authHeaders() helper
that uses the authenticated user's access_token when available, enabling
Row Level Security policies to identify the calling user correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Christian Vidal Wolf
2026-04-23 09:59:46 +02:00
co-authored by Claude Sonnet 4.6
parent 7a32605189
commit dec24f52f4
+28 -34
View File
@@ -1,5 +1,12 @@
const SUPABASE_URL = 'https://hwithddwaapyhnfwcesj.supabase.co';
const SUPABASE_KEY = 'sb_publishable_fGXkh0bSrAOqSk2jWKAzSg_NJD9YPCv';
const SUPABASE_ANON_KEY = 'sb_publishable_fGXkh0bSrAOqSk2jWKAzSg_NJD9YPCv';
function authHeaders(token?: string): Record<string, string> {
return {
'apikey': SUPABASE_ANON_KEY,
'Authorization': `Bearer ${token ?? SUPABASE_ANON_KEY}`,
};
}
export interface ExcelRow extends Array<any> {}
@@ -14,17 +21,14 @@ export async function getAllSyncedRows(token?: string): Promise<Record<string, S
`${SUPABASE_URL}/rest/v1/products?select=product_id,data,status&order=updated_at.desc`,
{
cache: 'no-store',
headers: {
'apikey': SUPABASE_KEY,
'Authorization': `Bearer ${SUPABASE_KEY}`
}
headers: authHeaders(token),
}
);
if (!response.ok) {
const errText = await response.text();
console.error('getAllSyncedRows failed:', response.status, errText);
return {};
const errText = await response.text();
console.error('getAllSyncedRows failed:', response.status, errText);
return {};
}
const rows = await response.json();
const result: Record<string, SyncedRow> = {};
@@ -46,9 +50,8 @@ export async function saveRowToSupabase(articleNo: string, rowData: ExcelRow, to
method: 'POST',
headers: {
'Content-Type': 'application/json',
'apikey': SUPABASE_KEY,
'Authorization': `Bearer ${SUPABASE_KEY}`,
'Prefer': 'resolution=merge-duplicates'
...authHeaders(token),
'Prefer': 'resolution=merge-duplicates',
},
body: JSON.stringify({
product_id: articleNo,
@@ -99,9 +102,8 @@ export async function saveHistoryEntry(
method: 'POST',
headers: {
'Content-Type': 'application/json',
'apikey': SUPABASE_KEY,
'Authorization': `Bearer ${SUPABASE_KEY}`,
'Prefer': 'return=minimal'
...authHeaders(token),
'Prefer': 'return=minimal',
},
body: JSON.stringify({
product_id: productId,
@@ -135,18 +137,14 @@ export async function getHistory(token?: string): Promise<HistoryEntry[]> {
`${SUPABASE_URL}/rest/v1/products_history?select=*&order=changed_at.desc&limit=100`,
{
cache: 'no-store',
headers: {
'apikey': SUPABASE_KEY,
// Try using only the ANON key just in case RLS or token expiry is failing silently
'Authorization': `Bearer ${SUPABASE_KEY}`
}
headers: authHeaders(token),
}
);
if (!response.ok) {
const err = await response.text();
return [{
id: 'error-' + Date.now(),
id: -1,
product_id: 'ERROR',
article_name: `Failed: ${response.status} ${err}`,
old_data: [],
@@ -159,13 +157,13 @@ export async function getHistory(token?: string): Promise<HistoryEntry[]> {
} catch (error: any) {
console.error('Error fetching history:', error);
return [{
id: 'error-' + Date.now(),
product_id: 'EXCEPTION',
article_name: `Message: ${error.message}`,
old_data: [],
new_data: [],
changed_at: new Date().toISOString(),
changed_by: 'system'
id: -2,
product_id: 'EXCEPTION',
article_name: `Message: ${error.message}`,
old_data: [],
new_data: [],
changed_at: new Date().toISOString(),
changed_by: 'system'
}];
}
}
@@ -176,10 +174,7 @@ export async function deleteHistoryEntry(id: string, token?: string): Promise<bo
`${SUPABASE_URL}/rest/v1/products_history?id=eq.${encodeURIComponent(id)}`,
{
method: 'DELETE',
headers: {
'apikey': SUPABASE_KEY,
'Authorization': `Bearer ${SUPABASE_KEY}`
}
headers: authHeaders(token),
}
);
@@ -198,9 +193,8 @@ export async function resetAllPendingRows(token?: string): Promise<boolean> {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'apikey': SUPABASE_KEY,
'Authorization': `Bearer ${SUPABASE_KEY}`,
'Prefer': 'return=minimal'
...authHeaders(token),
'Prefer': 'return=minimal',
},
body: JSON.stringify({ status: 'synced' })
}