Skip to content

Commit 67c7291

Browse files
fix(files): authorize metadata with internal user ids (#217)
1 parent d9a4971 commit 67c7291

2 files changed

Lines changed: 58 additions & 10 deletions

File tree

src/app/api/files/[fileId]/route.js

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,24 @@ export async function HEAD(request, { params } = {}) {
173173
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
174174
}
175175

176-
const userId = user.id;
177176
const { fileId } = await resolveRouteParams(params);
178177
const normalizedFileId = normalizeFileId(fileId);
179178
if (!normalizedFileId) {
180179
return missingFileIdResponse();
181180
}
182181

182+
const { data: internalUser, error: userError } = await supabase
183+
.from('users')
184+
.select('id')
185+
.eq('auth_user_id', user.id)
186+
.single();
187+
188+
if (userError || !internalUser) {
189+
return NextResponse.json({ error: 'User profile not found' }, { status: 404 });
190+
}
191+
192+
const userId = internalUser.id;
193+
183194
// Get file metadata from database
184195
const { data: fileData, error: fileError } = await supabase
185196
.from('encrypted_files')
@@ -189,13 +200,15 @@ export async function HEAD(request, { params } = {}) {
189200
created_at,
190201
messages!inner(
191202
conversation_id,
192-
conversation_participants!inner(
193-
user_id
203+
conversations!inner(
204+
conversation_participants!inner(
205+
user_id
206+
)
194207
)
195208
)
196209
`)
197210
.eq('id', normalizedFileId)
198-
.eq('messages.conversation_participants.user_id', userId)
211+
.eq('messages.conversations.conversation_participants.user_id', userId)
199212
.single();
200213

201214
if (fileError || !fileData) {
@@ -231,13 +244,25 @@ export async function POST(request, { params } = {}) {
231244
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
232245
}
233246

234-
const userId = user.id;
235247
const { fileId } = await resolveRouteParams(params);
236248
const normalizedFileId = normalizeFileId(fileId);
237249
if (!normalizedFileId) {
238250
return missingFileIdResponse();
239251
}
240252

253+
const authUserId = user.id;
254+
const { data: internalUser, error: userError } = await supabase
255+
.from('users')
256+
.select('id')
257+
.eq('auth_user_id', authUserId)
258+
.single();
259+
260+
if (userError || !internalUser) {
261+
return NextResponse.json({ error: 'User profile not found' }, { status: 404 });
262+
}
263+
264+
const userId = internalUser.id;
265+
241266
console.log(`📁 [FILE-INFO] Info request from user: ${userId} for file: ${fileId}`);
242267

243268
// Get file metadata from database
@@ -252,13 +277,15 @@ export async function POST(request, { params } = {}) {
252277
messages!inner(
253278
id,
254279
conversation_id,
255-
conversation_participants!inner(
256-
user_id
280+
conversations!inner(
281+
conversation_participants!inner(
282+
user_id
283+
)
257284
)
258285
)
259286
`)
260287
.eq('id', normalizedFileId)
261-
.eq('messages.conversation_participants.user_id', userId)
288+
.eq('messages.conversations.conversation_participants.user_id', userId)
262289
.single();
263290

264291
if (fileError || !fileData) {
@@ -274,7 +301,7 @@ export async function POST(request, { params } = {}) {
274301
encryptedMetadata: fileData.encrypted_metadata, // Client will decrypt
275302
createdAt: fileData.created_at,
276303
createdBy: fileData.created_by,
277-
isOwner: fileData.created_by === userId
304+
isOwner: fileData.created_by === authUserId || fileData.created_by === userId
278305
});
279306

280307
} catch (err) {

src/app/api/files/[fileId]/route.test.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,29 @@ function createFileQuery() {
4242
return query;
4343
}
4444

45+
function createUserQuery() {
46+
const query = {
47+
select: vi.fn(() => query),
48+
eq: vi.fn(() => query),
49+
single: vi.fn().mockResolvedValue({
50+
data: { id: 'user-1' },
51+
error: null
52+
})
53+
};
54+
return query;
55+
}
56+
4557
describe('/api/files/[fileId]', () => {
4658
beforeEach(() => {
4759
vi.resetModules();
4860
vi.clearAllMocks();
4961

5062
mocks.authGetUser.mockResolvedValue({
51-
data: { user: { id: 'user-1' } },
63+
data: { user: { id: 'auth-user-1' } },
5264
error: null
5365
});
5466
mocks.from.mockImplementation((table) => {
67+
if (table === 'users') return createUserQuery();
5568
if (table === 'encrypted_files') return createFileQuery();
5669
throw new Error(`Unexpected table: ${table}`);
5770
});
@@ -79,6 +92,10 @@ describe('/api/files/[fileId]', () => {
7992
expect(response.status).toBe(200);
8093
expect(response.headers.get('Content-Type')).toBe('application/octet-stream');
8194
expect(mocks.eq).toHaveBeenCalledWith('id', 'file-1');
95+
expect(mocks.eq).toHaveBeenCalledWith(
96+
'messages.conversations.conversation_participants.user_id',
97+
'user-1'
98+
);
8299
});
83100

84101
it('resolves async route params for POST metadata requests', async () => {
@@ -92,5 +109,9 @@ describe('/api/files/[fileId]', () => {
92109
expect(body.id).toBe('file-1');
93110
expect(body.conversationId).toBe('conversation-1');
94111
expect(mocks.eq).toHaveBeenCalledWith('id', 'file-1');
112+
expect(mocks.eq).toHaveBeenCalledWith(
113+
'messages.conversations.conversation_participants.user_id',
114+
'user-1'
115+
);
95116
});
96117
});

0 commit comments

Comments
 (0)