diff --git a/src/http-server.ts b/src/http-server.ts index 46f1fe8..2692c17 100644 --- a/src/http-server.ts +++ b/src/http-server.ts @@ -37,7 +37,11 @@ export interface HttpServerInstance { * 2. X-OpenProject-Api-Key: * 3. ?apiKey= query parameter */ -export function extractApiKey(req: Request, url: URL): string | undefined { +export function extractApiKey( + req: Request, + url: URL, + defaultApiKey?: string +): string | undefined { const authHeader = req.headers.get("authorization"); if (authHeader) { const match = authHeader.match(/^Bearer\s+(.+)$/i); @@ -56,6 +60,10 @@ export function extractApiKey(req: Request, url: URL): string | undefined { return queryKey.trim(); } + if (defaultApiKey && defaultApiKey.trim().length > 0) { + return defaultApiKey.trim(); + } + return undefined; } @@ -192,7 +200,7 @@ export async function startHttpServer(config: AppConfig): Promise { expect(res.headers.get("access-control-allow-methods")).toContain("GET"); }); + test("GET /sse falls back to config.apiKey when configured on server", async () => { + const singleTenantServer = await startHttpServer({ + baseUrl: "https://mock.openproject.example.com", + readOnly: true, + port: 0, + host: "127.0.0.1", + apiKey: "server-configured-key", + }); + + const res = await fetch(`http://127.0.0.1:${singleTenantServer.port}/sse`, { + headers: { Accept: "text/event-stream" }, + }); + expect(res.status).toBe(200); + const reader = res.body?.getReader(); + const { value } = await reader!.read(); + const text = new TextDecoder().decode(value); + expect(text).toContain("event: endpoint"); + await reader!.cancel(); + await singleTenantServer.stop(); + }); + test("concurrent clients with different API keys maintain isolated sessions", async () => { // Client A const resA = await fetch(`${serverUrl}/sse?apiKey=user-a-secret`, {