From 052265d6e64d2f4e47dab580f5250d729c41253d Mon Sep 17 00:00:00 2001 From: loookashow Date: Wed, 18 Mar 2026 14:08:41 +0100 Subject: [PATCH 1/2] fix: exclude query params from SecureKeyAuth signature payload --- src/auth/secure.ts | 5 ++--- tests/auth/secure.test.ts | 23 ++++++++++++++++++----- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/auth/secure.ts b/src/auth/secure.ts index 68dc047..b59ddd7 100644 --- a/src/auth/secure.ts +++ b/src/auth/secure.ts @@ -42,10 +42,9 @@ export class SecureKeyAuth implements AuthStrategy { let path: string; try { const parsed = new URL(request.url); + // Query parameters are excluded from the signature payload to match + // server-side verification, which signs only the request path. path = parsed.pathname || '/'; - if (parsed.search) { - path = `${path}${parsed.search}`; - } } catch { path = request.path || '/'; } diff --git a/tests/auth/secure.test.ts b/tests/auth/secure.test.ts index 07e480e..83b3006 100644 --- a/tests/auth/secure.test.ts +++ b/tests/auth/secure.test.ts @@ -1,4 +1,4 @@ -import { generateKeyPairSync } from 'node:crypto'; +import { createHash, createVerify, generateKeyPairSync } from 'node:crypto'; import { describe, expect, it } from 'vitest'; import { SecureKeyAuth } from '../../src/auth/secure.js'; import type { RequestData } from '../../src/auth/types.js'; @@ -14,7 +14,7 @@ function generateTestKeyPair() { const pubKeyB64 = Buffer.from(publicKey as string) .toString('base64') .substring(0, 16); - return { publicKey: pubKeyB64, privateKey: privateKeyB64 }; + return { publicKey: pubKeyB64, privateKey: privateKeyB64, verifyKey: publicKey as string }; } const dummyRequest: RequestData = { @@ -92,7 +92,7 @@ describe('SecureKeyAuth', () => { expect(headers1.Authorization).not.toBe(headers2.Authorization); }); - it('includes query string in signed path', () => { + it('ignores query string in signed path', () => { const keys = generateTestKeyPair(); const fixedDate = new Date('2024-06-15T12:00:00Z'); const auth = new SecureKeyAuth(keys.publicKey, keys.privateKey, { @@ -106,8 +106,21 @@ describe('SecureKeyAuth', () => { path: '/v1/test?page=2', }); - // Different query string means different signature - expect(headers1.Authorization).not.toBe(headers2.Authorization); + const signature1 = Buffer.from(headers1.Authorization.split(':', 2)[1], 'base64'); + const signature2 = Buffer.from(headers2.Authorization.split(':', 2)[1], 'base64'); + const bodyHash = createHash('sha256').update(dummyRequest.body).digest('hex'); + const expectedPayload = `/v1/test|${bodyHash}|2024-06-15T12:00:00Z`; + + const verifier1 = createVerify('SHA256'); + verifier1.update(expectedPayload); + verifier1.end(); + + const verifier2 = createVerify('SHA256'); + verifier2.update(expectedPayload); + verifier2.end(); + + expect(verifier1.verify(keys.verifyKey, signature1)).toBe(true); + expect(verifier2.verify(keys.verifyKey, signature2)).toBe(true); }); it('throws on empty publicKey', () => { From dad49e40a4bb7e8eaaecb75898d440fc69b245e0 Mon Sep 17 00:00:00 2001 From: loookashow Date: Wed, 18 Mar 2026 14:10:36 +0100 Subject: [PATCH 2/2] chore(release): bump @foxnose/sdk to 0.2.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7b71959..1ec284e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@foxnose/sdk", - "version": "0.2.2", + "version": "0.2.3", "description": "Official FoxNose SDK for TypeScript and JavaScript", "license": "Apache-2.0", "type": "module",