diff --git a/.changeset/wild-suns-bearer-space.md b/.changeset/wild-suns-bearer-space.md new file mode 100644 index 0000000000..71a6805b40 --- /dev/null +++ b/.changeset/wild-suns-bearer-space.md @@ -0,0 +1,5 @@ +--- +"effect": patch +--- + +Fix `HttpApiSecurity` bearer/http credential decoding diff --git a/packages/effect/src/unstable/httpapi/HttpApiBuilder.ts b/packages/effect/src/unstable/httpapi/HttpApiBuilder.ts index 67ed47e3a8..8ca5780fa5 100644 --- a/packages/effect/src/unstable/httpapi/HttpApiBuilder.ts +++ b/packages/effect/src/unstable/httpapi/HttpApiBuilder.ts @@ -446,7 +446,8 @@ export const securityDecode = case "Http": { return Effect.map( HttpServerRequest, - (request) => Redacted.make((request.headers.authorization ?? "").slice(self.schemeLength)) as any + // schemeLength + space + (request) => Redacted.make((request.headers.authorization ?? "").slice(self.schemeLength + 1)) as any ) } case "ApiKey": { diff --git a/packages/effect/test/unstable/httpapi/HttpApiSecurity.test.ts b/packages/effect/test/unstable/httpapi/HttpApiSecurity.test.ts new file mode 100644 index 0000000000..1fbfcfb8d7 --- /dev/null +++ b/packages/effect/test/unstable/httpapi/HttpApiSecurity.test.ts @@ -0,0 +1,38 @@ +import { describe, it } from "@effect/vitest" +import { strictEqual } from "@effect/vitest/utils" +import { Effect, Redacted } from "effect" +import { HttpClientRequest, HttpServerRequest } from "effect/unstable/http" +import { HttpApiBuilder, HttpApiSecurity } from "effect/unstable/httpapi" + +describe("HttpApiSecurity", () => { + describe("securityDecode", () => { + it.effect("decodes a bearer token without a leading space", () => + Effect.gen(function*() { + const token = "abc123" + const { headers } = HttpClientRequest.get("http://localhost/").pipe( + HttpClientRequest.bearerToken(token) + ) + const credential = yield* HttpApiBuilder.securityDecode(HttpApiSecurity.bearer).pipe( + Effect.provideService( + HttpServerRequest.HttpServerRequest, + HttpServerRequest.fromWeb(new Request("http://localhost/", { headers })) + ), + Effect.provideService(HttpServerRequest.ParsedSearchParams, {}) + ) + + strictEqual(Redacted.value(credential), token) + })) + + it.effect("decodes a custom http scheme without a leading space", () => + Effect.gen(function*() { + const credential = yield* HttpApiBuilder.securityDecode(HttpApiSecurity.http({ scheme: "Token" })).pipe( + Effect.provideService( + HttpServerRequest.HttpServerRequest, + HttpServerRequest.fromWeb(new Request("http://localhost/", { headers: { authorization: "Token abc123" } })) + ), + Effect.provideService(HttpServerRequest.ParsedSearchParams, {}) + ) + strictEqual(Redacted.value(credential), "abc123") + })) + }) +})