From 4121597fa2e4e0a6a61a179bdd54663b5273b02a Mon Sep 17 00:00:00 2001 From: Jonas Perusquia Morales Date: Tue, 24 Mar 2026 12:20:17 -0600 Subject: [PATCH 1/2] fix: Match Bearer case-insensitively and trim whitespace(s) --- app/src/Api.ts | 2 +- app/src/auth/authenticate/Authenticator.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/Api.ts b/app/src/Api.ts index adf69e6e..515d5ace 100644 --- a/app/src/Api.ts +++ b/app/src/Api.ts @@ -121,7 +121,7 @@ export class Api { } // try authorization header - const headerToken = this.options.headers.get("authorization")?.replace("Bearer ", ""); + const headerToken = this.options.headers.get("authorization")?.replace(/^Bearer\s+/i, ""); if (headerToken) { this.token_transport = "header"; this.updateToken(headerToken); diff --git a/app/src/auth/authenticate/Authenticator.ts b/app/src/auth/authenticate/Authenticator.ts index 03f13e9e..d0cb083f 100644 --- a/app/src/auth/authenticate/Authenticator.ts +++ b/app/src/auth/authenticate/Authenticator.ts @@ -430,7 +430,7 @@ export class Authenticator< let token: string | undefined; if (headers.has("Authorization")) { const bearerHeader = String(headers.get("Authorization")); - token = bearerHeader.replace("Bearer ", ""); + token = bearerHeader.replace(/^Bearer\s+/i, ""); } else { const context = is_context ? (c as Context) : ({ req: { raw: { headers } } } as Context); token = await this.getAuthCookie(context); From 9219bf3b3c5192ac0708160464f7c2de984ce611 Mon Sep 17 00:00:00 2001 From: dswbx Date: Sat, 28 Mar 2026 10:33:24 +0100 Subject: [PATCH 2/2] test(auth, api): ensure bearer token is case-insensitive --- app/__test__/api/Api.spec.ts | 6 ++++++ app/__test__/auth/Authenticator.spec.ts | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/app/__test__/api/Api.spec.ts b/app/__test__/api/Api.spec.ts index 4384928d..7da7fbc0 100644 --- a/app/__test__/api/Api.spec.ts +++ b/app/__test__/api/Api.spec.ts @@ -70,4 +70,10 @@ describe("Api", async () => { expect(params.token_transport).toBe("header"); expect(params.host).toBe("http://another.com"); }); + + it("should extract tokens case insensitive", async () => { + const token = await sign({ sub: "test" }, "1234"); + expect(new Api({ headers: new Headers({ Authorization: `Bearer ${token}` }) }).getAuthState().token).toBe(token); + expect(new Api({ headers: new Headers({ Authorization: `bearer ${token}` }) }).getAuthState().token).toBe(token); + }) }); diff --git a/app/__test__/auth/Authenticator.spec.ts b/app/__test__/auth/Authenticator.spec.ts index fcf8e5cb..171614dc 100644 --- a/app/__test__/auth/Authenticator.spec.ts +++ b/app/__test__/auth/Authenticator.spec.ts @@ -38,4 +38,27 @@ describe("Authenticator", async () => { expect(cookie).toStartWith("auth="); expect(cookie).toEndWith("HttpOnly; Secure; SameSite=Strict"); }); + + test("bearer token is case insensitive", async () => { + const auth = new Authenticator({}, null as any, { + jwt: { + secret: "secret", + fields: ["sub"], + }, + cookie: { + sameSite: "strict", + }, + }); + const token = await auth.jwt({ sub: "test" }); + + const res = await auth.resolveAuthFromRequest(new Headers({ + Authorization: `Bearer ${token}`, + })); + expect((res as any).sub).toBe("test") + + const res2 = await auth.resolveAuthFromRequest(new Headers({ + Authorization: `bearer ${token}`, + })); + expect((res2 as any).sub).toBe("test") + }) });