diff --git a/library/agent/api-discovery/getApiAuthType.test.ts b/library/agent/api-discovery/getApiAuthType.test.ts index 06a9d1bde..b101ff1db 100644 --- a/library/agent/api-discovery/getApiAuthType.test.ts +++ b/library/agent/api-discovery/getApiAuthType.test.ts @@ -41,6 +41,17 @@ t.test("it detects api keys", async (t) => { ]); }); +t.test("it doesn't add the same api key again", async () => { + t.same(get(getContext({ "api-key": "token", "api-key-a": "token" })), [ + { type: "apiKey", in: "header", name: "api-key" }, + { type: "apiKey", in: "header", name: "api-key-a" }, + ]); + t.same(get(getContext({ "api-key": "token", "api-key-apikey": "token" })), [ + { type: "apiKey", in: "header", name: "api-key" }, + { type: "apiKey", in: "header", name: "api-key-apikey" }, + ]); +}); + t.test("it detects auth cookies", async (t) => { t.same(get(getContext({}, { "api-key": "token" })), [ { type: "apiKey", in: "cookie", name: "api-key" }, @@ -54,6 +65,49 @@ t.test("it detects auth cookies", async (t) => { ]); }); +t.test("it detects multiple auth cookies", async (t) => { + t.same(get(getContext({}, { "api-key": "token", "api-key-a": "token" })), [ + { type: "apiKey", in: "cookie", name: "api-key" }, + { type: "apiKey", in: "cookie", name: "api-key-a" }, + ]); + t.same(get(getContext({}, { "api-key": "token", "api-key-sid": "token" })), [ + { type: "apiKey", in: "cookie", name: "api-key-sid" }, + { type: "apiKey", in: "cookie", name: "api-key" }, + ]); +}); + +t.test("it detects cookies and api keys at same time", async (t) => { + t.same( + get( + getContext( + { + "api-key": "token", + }, + { "api-key": "token" } + ) + ), + [ + { type: "apiKey", in: "header", name: "api-key" }, + { type: "apiKey", in: "cookie", name: "api-key" }, + ] + ); + + t.same( + get( + getContext( + { + "x-api-key": "token", + }, + { "api-key": "token" } + ) + ), + [ + { type: "apiKey", in: "header", name: "x-api-key" }, + { type: "apiKey", in: "cookie", name: "api-key" }, + ] + ); +}); + t.test("no auth", async (t) => { t.same(get(getContext()), undefined); t.same(get(getContext({})), undefined); diff --git a/library/agent/api-discovery/getApiAuthType.ts b/library/agent/api-discovery/getApiAuthType.ts index 770a23212..b024db919 100644 --- a/library/agent/api-discovery/getApiAuthType.ts +++ b/library/agent/api-discovery/getApiAuthType.ts @@ -18,24 +18,13 @@ export type APIAuthType = { }; // Incoming request headers are lowercase in Node.js -const commonApiKeyHeaderNames = [ - "x-api-key", - "api-key", - "apikey", - "x-token", - "token", -]; +const commonApiKeyHeaderNames = ["api-key", "apikey", "token"]; const commonAuthCookieNames = [ "auth", "session", "jwt", - "token", "sid", - "connect.sid", - "auth_token", - "access_token", - "refresh_token", ...commonApiKeyHeaderNames, ]; @@ -82,12 +71,13 @@ function getAuthorizationHeaderType( if (authHeader.includes(" ")) { const [type, value] = authHeader.split(" "); - if (typeof type === "string" && typeof value === "string") { + if (type && value) { if (isHTTPAuthScheme(type)) { return { type: "http", scheme: type.toLowerCase() as HTTPAuthScheme }; } } } + // Default to apiKey if the auth type is not recognized return { type: "apiKey", in: "header", name: "Authorization" }; } @@ -98,10 +88,20 @@ function getAuthorizationHeaderType( function findApiKeys(context: Context): APIAuthType[] { const result: APIAuthType[] = []; + const headerNames = Object.keys(context.headers); for (const header of commonApiKeyHeaderNames) { - if (context.headers[header]) { - result.push({ type: "apiKey", in: "header", name: header }); - } + const matches = headerNames.filter((name) => + name.toLowerCase().includes(header) + ); + + matches.forEach((match) => { + const alreadyAdded = result.some( + (authType) => authType.name === match && authType.in === "header" + ); + if (!alreadyAdded) { + result.push({ type: "apiKey", in: "header", name: match }); + } + }); } if ( @@ -110,11 +110,19 @@ function findApiKeys(context: Context): APIAuthType[] { !Array.isArray(context.cookies) && Object.keys(context.cookies).length > 0 ) { - const relevantCookies = Object.keys(context.cookies).filter((cookieName) => - commonAuthCookieNames.includes(cookieName.toLowerCase()) - ); - for (const cookie of relevantCookies) { - result.push({ type: "apiKey", in: "cookie", name: cookie }); + for (const cookie of commonAuthCookieNames) { + const matches = Object.keys(context.cookies).filter((name) => + name.toLowerCase().includes(cookie.toLowerCase()) + ); + + matches.forEach((match) => { + const alreadyAdded = result.some( + (authType) => authType.name === match && authType.in === "cookie" + ); + if (!alreadyAdded) { + result.push({ type: "apiKey", in: "cookie", name: match }); + } + }); } }