diff --git a/src/app/api/auth/backup-pin/route.js b/src/app/api/auth/backup-pin/route.js index 40ce4324..a8433cd6 100644 --- a/src/app/api/auth/backup-pin/route.js +++ b/src/app/api/auth/backup-pin/route.js @@ -15,6 +15,15 @@ function getServiceRoleClient() { const supabaseClient = createClient(process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY); +function getBearerToken(authHeader) { + if (typeof authHeader !== 'string') return null; + + const match = authHeader.match(/^Bearer\s+(.+)$/i); + const token = match?.[1]?.trim(); + + return token || null; +} + /** * Authenticate user from request cookies * @param {Request} request @@ -24,8 +33,8 @@ async function authenticateUser(request) { try { // Try Authorization header first (used during registration) const authHeader = request.headers.get('authorization'); - if (authHeader?.startsWith('Bearer ')) { - const token = authHeader.substring(7); + const token = getBearerToken(authHeader); + if (token) { const { data: { user }, error } = await supabaseClient.auth.getUser(token); if (!error && user) { return { user }; diff --git a/src/app/api/auth/backup-pin/route.test.js b/src/app/api/auth/backup-pin/route.test.js index 3345a211..5ba691d7 100644 --- a/src/app/api/auth/backup-pin/route.test.js +++ b/src/app/api/auth/backup-pin/route.test.js @@ -69,4 +69,35 @@ describe('backup PIN cookie authentication', () => { expect(body).toEqual({ hasPin: true }); expect(mocks.authGetUser).toHaveBeenCalledWith('access-token'); }); + + it('normalizes bearer scheme casing and extra spaces', async () => { + const { GET } = await import('./route.js'); + const response = await GET( + new Request('https://qrypt.chat/api/auth/backup-pin', { + headers: { + authorization: 'bearer access-token ' + } + }) + ); + const body = await response.json(); + + expect(response.status).toBe(200); + expect(body).toEqual({ hasPin: true }); + expect(mocks.authGetUser).toHaveBeenCalledWith('access-token'); + }); + + it('ignores an empty bearer header and falls back to cookies', async () => { + const { GET } = await import('./route.js'); + const response = await GET( + new Request('https://qrypt.chat/api/auth/backup-pin', { + headers: { + authorization: 'Bearer ', + cookie: `sb-xydzwxwsbgmznthiiscl-auth-token=${cookieValue('cookie-token')}` + } + }) + ); + + expect(response.status).toBe(200); + expect(mocks.authGetUser).toHaveBeenCalledWith('cookie-token'); + }); });