diff --git a/src/node/cli.ts b/src/node/cli.ts index 9eb6e5163e8a..a07a18b0a260 100644 --- a/src/node/cli.ts +++ b/src/node/cli.ts @@ -84,6 +84,7 @@ export interface UserProvidedArgs extends UserProvidedCodeArgs { "trusted-origins"?: string[] version?: boolean "proxy-domain"?: string[] + "skip-auth-preflight"?: boolean "reuse-window"?: boolean "new-window"?: boolean "ignore-last-opened"?: boolean @@ -252,6 +253,10 @@ export const options: Options> = { description: "GitHub authentication token (can only be passed in via $GITHUB_TOKEN or the config file).", }, "proxy-domain": { type: "string[]", description: "Domain used for proxying ports." }, + "skip-auth-preflight": { + type: "boolean", + description: "Allows preflight requests through proxy without authentication.", + }, "ignore-last-opened": { type: "boolean", short: "e", diff --git a/src/node/main.ts b/src/node/main.ts index 990a7af792b1..04e4470b9088 100644 --- a/src/node/main.ts +++ b/src/node/main.ts @@ -163,6 +163,9 @@ export const runCodeServer = async ( logger.info(` - ${plural(args["proxy-domain"].length, "Proxying the following domain")}:`) args["proxy-domain"].forEach((domain) => logger.info(` - ${domain}`)) } + if (args["skip-auth-preflight"]) { + logger.info(" - Skipping authentication for preflight requests") + } if (process.env.VSCODE_PROXY_URI) { logger.info(`Using proxy URI in PORTS tab: ${process.env.VSCODE_PROXY_URI}`) } diff --git a/src/node/routes/domainProxy.ts b/src/node/routes/domainProxy.ts index 0a9bb4a324f7..6ffee67fa002 100644 --- a/src/node/routes/domainProxy.ts +++ b/src/node/routes/domainProxy.ts @@ -61,6 +61,11 @@ router.all(/.*/, async (req, res, next) => { ensureProxyEnabled(req) + if (req.method === "OPTIONS" && req.args["skip-auth-preflight"]) { + // Allow preflight requests with `skip-auth-preflight` flag + return next() + } + // Must be authenticated to use the proxy. const isAuthenticated = await authenticated(req) if (!isAuthenticated) { diff --git a/src/node/routes/pathProxy.ts b/src/node/routes/pathProxy.ts index bb8efd40d832..254c5e623a79 100644 --- a/src/node/routes/pathProxy.ts +++ b/src/node/routes/pathProxy.ts @@ -26,7 +26,9 @@ export async function proxy( ): Promise { ensureProxyEnabled(req) - if (!(await authenticated(req))) { + if (req.method === "OPTIONS" && req.args["skip-auth-preflight"]) { + // Allow preflight requests with `skip-auth-preflight` flag + } else if (!(await authenticated(req))) { // If visiting the root (/:port only) redirect to the login page. if (!req.params.path || req.params.path === "/") { const to = self(req) diff --git a/test/unit/node/cli.test.ts b/test/unit/node/cli.test.ts index e596549da100..552576fac4c9 100644 --- a/test/unit/node/cli.test.ts +++ b/test/unit/node/cli.test.ts @@ -108,6 +108,8 @@ describe("parser", () => { ["--abs-proxy-base-path", "/codeserver/app1"], + "--skip-auth-preflight", + ["--session-socket", "/tmp/override-code-server-ipc-socket"], ["--host", "0.0.0.0"], @@ -146,6 +148,7 @@ describe("parser", () => { "bind-addr": "192.169.0.1:8080", "session-socket": "/tmp/override-code-server-ipc-socket", "abs-proxy-base-path": "/codeserver/app1", + "skip-auth-preflight": true, }) }) diff --git a/test/unit/node/proxy.test.ts b/test/unit/node/proxy.test.ts index 186cd475b3e2..b3509ed640df 100644 --- a/test/unit/node/proxy.test.ts +++ b/test/unit/node/proxy.test.ts @@ -268,6 +268,21 @@ describe("proxy", () => { const text = await resp.text() expect(text).toBe("app being served behind a prefixed path") }) + + it("should not allow OPTIONS without authentication by default", async () => { + process.env.PASSWORD = "test" + codeServer = await integration.setup(["--auth=password"]) + const resp = await codeServer.fetch(proxyPath, { method: "OPTIONS" }) + expect(resp.status).toBe(401) + }) + + it("should allow OPTIONS with `skip-auth-preflight` flag", async () => { + process.env.PASSWORD = "test" + codeServer = await integration.setup(["--auth=password", "--skip-auth-preflight"]) + e.post("/wsup", (req, res) => {}) + const resp = await codeServer.fetch(proxyPath, { method: "OPTIONS" }) + expect(resp.status).toBe(200) + }) }) // NOTE@jsjoeio