fix(server): allow authenticated CORS preflight

This commit is contained in:
Brendan Allan
2026-07-21 09:53:33 +08:00
parent fc7e4cf93e
commit a05e1b174f
2 changed files with 49 additions and 1 deletions
+7 -1
View File
@@ -10,6 +10,7 @@ import { HttpMiddleware, HttpRouter, HttpServer, HttpServerRequest, HttpServerRe
import { randomUUID } from "node:crypto"
import { createServer } from "node:http"
import { ServerAuth } from "./auth"
import { isAllowedCorsOrigin } from "./cors"
import { authorizedRequest } from "./middleware/authorization"
import { withoutParentSpan } from "./request-tracing"
import { createRoutes } from "./routes"
@@ -48,7 +49,12 @@ export const start = Effect.fn("ServerProcess.start")(function* <E, R>(
const application = yield* Ref.make(Option.none<App>())
// Request fibers may continue inbound trace context, but must not inherit the server startup parent.
yield* bound.http
.serve(dispatch(password, status, application, shutdown), HttpMiddleware.logger)
.serve(
dispatch(password, status, application, shutdown).pipe(
HttpMiddleware.cors({ allowedOrigins: isAllowedCorsOrigin, maxAge: 86_400 }),
),
HttpMiddleware.logger,
)
.pipe(withoutParentSpan)
if (lifecycle)
yield* lifecycle.onListen(bound.http.address, Deferred.succeed(shutdown, undefined).pipe(Effect.asVoid)).pipe(
+42
View File
@@ -0,0 +1,42 @@
import { expect } from "bun:test"
import { Effect } from "effect"
import { HttpServer } from "effect/unstable/http"
import { it } from "../../core/test/lib/effect"
import { ServerProcess } from "../src/process"
it.live("allows browser preflight requests without credentials", () =>
Effect.gen(function* () {
const server = yield* ServerProcess.start<never, never>({
hostname: "127.0.0.1",
port: 0,
password: "secret",
database: { path: ":memory:" },
})
const response = yield* Effect.promise(() =>
fetch(new URL("/api/health", HttpServer.formatAddress(server.address)), {
method: "OPTIONS",
headers: {
origin: "http://localhost:3000",
"access-control-request-method": "GET",
"access-control-request-headers": "authorization",
},
}),
)
expect(response.status).toBe(204)
expect(response.headers.get("access-control-allow-origin")).toBe("http://localhost:3000")
expect(response.headers.get("access-control-allow-headers")).toBe("authorization")
const health = yield* Effect.promise(() =>
fetch(new URL("/api/health", HttpServer.formatAddress(server.address)), {
headers: {
authorization: `Basic ${btoa("opencode:secret")}`,
origin: "http://localhost:3000",
},
}),
)
expect(health.status).toBe(200)
expect(health.headers.get("access-control-allow-origin")).toBe("http://localhost:3000")
}),
)