From 5d72f1677b0767e04c95dfccad254b4dc45311f3 Mon Sep 17 00:00:00 2001 From: aiirvizionz Date: Sat, 11 Jul 2026 10:25:15 -0600 Subject: [PATCH] Parse key backup auth cookies without requiring spaces --- src/app/api/auth/key-backup/route.js | 2 +- src/app/api/auth/key-backup/route.test.js | 82 +++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 src/app/api/auth/key-backup/route.test.js diff --git a/src/app/api/auth/key-backup/route.js b/src/app/api/auth/key-backup/route.js index 4d41906..3b00161 100644 --- a/src/app/api/auth/key-backup/route.js +++ b/src/app/api/auth/key-backup/route.js @@ -41,7 +41,7 @@ async function authenticateUser(request) { } const cookies = Object.fromEntries( - cookieHeader.split('; ').map(cookie => { + cookieHeader.split(/;\s*/).map(cookie => { const [name, value] = cookie.split('='); return [name, decodeURIComponent(value)]; }) diff --git a/src/app/api/auth/key-backup/route.test.js b/src/app/api/auth/key-backup/route.test.js new file mode 100644 index 0000000..c05d264 --- /dev/null +++ b/src/app/api/auth/key-backup/route.test.js @@ -0,0 +1,82 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +const mocks = vi.hoisted(() => ({ + authGetUser: vi.fn(), + serviceFrom: vi.fn() +})); + +vi.mock('@supabase/supabase-js', () => ({ + createClient: vi.fn(() => ({ + auth: { + getUser: mocks.authGetUser + } + })) +})); + +vi.mock('@/lib/supabase/service-role.js', () => ({ + createServiceRoleClient: vi.fn(() => ({ + from: mocks.serviceFrom + })) +})); + +function cookieValue(token) { + return `base64-${Buffer.from(JSON.stringify({ access_token: token })).toString('base64')}`; +} + +function createBackupQuery() { + const query = { + select: vi.fn(() => query), + eq: vi.fn(() => query), + single: vi.fn(() => + Promise.resolve({ + data: { + encrypted_keys: '{"version":1}', + created_at: '2026-07-11T00:00:00.000Z', + updated_at: '2026-07-11T00:00:00.000Z' + }, + error: null + }) + ) + }; + return query; +} + +describe('key backup cookie authentication', () => { + beforeEach(() => { + vi.resetModules(); + vi.clearAllMocks(); + process.env.NEXT_PUBLIC_SUPABASE_URL = 'https://example.supabase.co'; + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY = 'anon-key'; + + mocks.authGetUser.mockResolvedValue({ + data: { user: { id: 'auth-user-id' } }, + error: null + }); + mocks.serviceFrom.mockImplementation((table) => { + if (table === 'key_backups') return createBackupQuery(); + throw new Error(`Unexpected table: ${table}`); + }); + }); + + it('accepts valid cookie headers without a space after semicolons', async () => { + const { GET } = await import('./route.js'); + const response = await GET( + new Request('https://qrypt.chat/api/auth/key-backup', { + headers: { + cookie: `sb-xydzwxwsbgmznthiiscl-auth-token=${cookieValue('access-token')};session=ignored` + } + }) + ); + const body = await response.json(); + + expect(response.status).toBe(200); + expect(body).toEqual({ + backup: { + encrypted_keys: '{"version":1}', + created_at: '2026-07-11T00:00:00.000Z', + updated_at: '2026-07-11T00:00:00.000Z' + } + }); + expect(mocks.authGetUser).toHaveBeenCalledWith('access-token'); + }); +});