Skip to content

[codex] Require tokens for gateway API auth#612

Open
furukama wants to merge 2 commits intomainfrom
codex/fix-loopback-auth-bypass
Open

[codex] Require tokens for gateway API auth#612
furukama wants to merge 2 commits intomainfrom
codex/fix-loopback-auth-bypass

Conversation

@furukama
Copy link
Copy Markdown
Contributor

Summary

  • Remove loopback-address fallback authentication from gateway API requests when WEB_API_TOKEN is unset.
  • Require explicit GATEWAY_API_TOKEN, WEB_API_TOKEN, or existing allowed query-token auth for API access.
  • Add regression coverage for loopback requests, forwarded loopback headers, empty bearer tokens, and explicit gateway-token auth.

Root Cause

hasApiAuth treated the raw socket peer as authenticated when WEB_API_TOKEN was unset. That made local peers bypass bearer-token auth, and it was especially risky when the gateway was deployed behind a proxy or local process boundaries were compromised.

Validation

  • ./node_modules/.bin/vitest tests/gateway-http-server.test.ts --run
  • npm run typecheck
  • npm run lint
  • npx biome check src/gateway/gateway-http-server.ts tests/gateway-http-server.test.ts

@furukama furukama marked this pull request as ready for review April 27, 2026 21:18
Copilot AI review requested due to automatic review settings April 27, 2026 21:18
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens gateway API authentication by removing the implicit “localhost/loopback is authenticated when WEB_API_TOKEN is unset” behavior, and adds regression tests to ensure unauthenticated requests are rejected and token-based auth paths behave as expected.

Changes:

  • Remove loopback-address fallback from hasApiAuth(); API access now requires WEB_API_TOKEN, GATEWAY_API_TOKEN, and/or allowed query-token auth.
  • Update the gateway HTTP server test harness to default to a gateway token and explicitly mark unauthenticated requests in relevant cases.
  • Add/adjust regression tests for loopback requests, empty bearer tokens, and terminal websocket upgrades with explicit request auth.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/gateway/gateway-http-server.ts Removes loopback-based API auth fallback by updating hasApiAuth() logic.
tests/gateway-http-server.test.ts Updates request helper/default token mocking and adds/updates auth regression coverage.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 666 to 672
const authHeader = req.headers.authorization || '';
const gatewayTokenMatch =
Boolean(GATEWAY_API_TOKEN) && authHeader === `Bearer ${GATEWAY_API_TOKEN}`;
if (opts?.allowQueryToken && url && hasQueryToken(url)) return true;

if (!WEB_API_TOKEN) {
return gatewayTokenMatch || isLoopbackAddress(req.socket.remoteAddress);
}
if (authHeader === `Bearer ${WEB_API_TOKEN}`) return true;
if (WEB_API_TOKEN && authHeader === `Bearer ${WEB_API_TOKEN}`) return true;
return gatewayTokenMatch;
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With loopback fallback removed, external docs that say “if WEB_API_TOKEN is unset, loopback requests can omit Authorization” are now outdated (e.g. docs/reference/commands.md mentions this). Please update the relevant docs alongside this behavior change so users don’t rely on the old localhost-bypass guidance.

Copilot uses AI. Check for mistakes.
Comment on lines 314 to +322
return Object.assign(Readable.from(chunks), {
method: params.method || 'GET',
url: params.url,
headers: params.headers || {},
headers: {
...(params.authenticated === false
? {}
: { authorization: `Bearer ${DEFAULT_TEST_GATEWAY_API_TOKEN}` }),
...(params.headers || {}),
},
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makeRequest() now injects an Authorization header by default, which can silently change the meaning of many tests (e.g., tests meant to exercise query-token auth or “no-auth” paths will actually be Bearer-authenticated unless authenticated: false is set). Consider making the default be no Authorization header (and requiring tests to opt-in), or replace authenticated with an explicit auth/authToken option to avoid accidental coverage gaps.

Copilot uses AI. Check for mistakes.
remoteAddress: '203.0.113.10',
headers: { 'x-forwarded-for': '127.0.0.1' },
authenticated: false,
});
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new loopback-auth regression test includes an x-forwarded-for: 127.0.0.1 header but leaves the socket remoteAddress at its default (also loopback). If the intent is to cover “forwarded loopback headers don’t grant auth”, set remoteAddress to a non-loopback value and keep x-forwarded-for loopback so the test actually exercises that scenario.

Suggested change
});
});
Object.defineProperty(req.socket, 'remoteAddress', {
value: '203.0.113.10',
configurable: true,
});

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants