From 8661c271e922760c0e8a5ab9a62e0dd33dacc8a0 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Thu, 12 Mar 2026 14:56:40 -0400 Subject: [PATCH] Gateway: preserve trusted-proxy browser scopes --- .../server.auth.browser-hardening.test.ts | 42 +++++++++++++++++++ .../server/ws-connection/message-handler.ts | 2 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/gateway/server.auth.browser-hardening.test.ts b/src/gateway/server.auth.browser-hardening.test.ts index c4060716b..c31fb7c19 100644 --- a/src/gateway/server.auth.browser-hardening.test.ts +++ b/src/gateway/server.auth.browser-hardening.test.ts @@ -15,6 +15,7 @@ import { connectOk, installGatewayTestHooks, readConnectChallengeNonce, + rpcReq, testState, trackConnectChallengeNonce, withGatewayServer, @@ -150,6 +151,47 @@ describe("gateway auth browser hardening", () => { }); }); + test("preserves scopes for trusted-proxy non-control-ui browser sessions", async () => { + const { writeConfigFile } = await import("../config/config.js"); + await writeConfigFile({ + gateway: { + auth: { + mode: "trusted-proxy", + trustedProxy: { + userHeader: "x-forwarded-user", + requiredHeaders: ["x-forwarded-proto"], + }, + }, + trustedProxies: ["127.0.0.1"], + controlUi: { + allowedOrigins: [ALLOWED_BROWSER_ORIGIN], + }, + }, + }); + + await withGatewayServer(async ({ port }) => { + const ws = await openWs(port, { + origin: ALLOWED_BROWSER_ORIGIN, + "x-forwarded-for": "203.0.113.50", + "x-forwarded-proto": "https", + "x-forwarded-user": "operator@example.com", + }); + try { + const payload = await connectOk(ws, { + client: TEST_OPERATOR_CLIENT, + device: null, + scopes: ["operator.read"], + }); + expect(payload.type).toBe("hello-ok"); + + const status = await rpcReq(ws, "status"); + expect(status.ok).toBe(true); + } finally { + ws.close(); + } + }); + }); + test.each([ { name: "rejects disallowed origins", diff --git a/src/gateway/server/ws-connection/message-handler.ts b/src/gateway/server/ws-connection/message-handler.ts index d3d98da46..d327cd683 100644 --- a/src/gateway/server/ws-connection/message-handler.ts +++ b/src/gateway/server/ws-connection/message-handler.ts @@ -526,7 +526,7 @@ export function attachGatewayWsMessageHandler(params: { hasSharedAuth, isLocalClient, }); - if (!device && (!isControlUi || decision.kind !== "allow")) { + if (!device && decision.kind !== "allow") { clearUnboundScopes(); } if (decision.kind === "allow") {