Skip to content

fix(api-proxy): strip accidental "Bearer " prefix in BYOK Copilot auth token#2521

Merged
lpcox merged 3 commits intomainfrom
copilot/fix-badly-formatted-authorization-header
May 4, 2026
Merged

fix(api-proxy): strip accidental "Bearer " prefix in BYOK Copilot auth token#2521
lpcox merged 3 commits intomainfrom
copilot/fix-badly-formatted-authorization-header

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 4, 2026

In BYOK mode (COPILOT_PROVIDER_API_KEY + COPILOT_PROVIDER_BASE_URL), if COPILOT_API_KEY was set with an accidental "Bearer " prefix, the sidecar would forward Authorization: ****** <key> — rejected by external providers (e.g. OpenRouter) with 400 Authorization header is badly formatted.

Changes

containers/api-proxy/providers/copilot.js

  • resolveCopilotAuthToken: Strip `****** prefix (case-insensitive, tolerates leading whitespace) before returning the token, preventing double-prefixing downstream.
  • createCopilotAdapter: Apply the same sanitization to the locally-scoped githubToken/apiKey used in getAuthHeaders for the /models special-case path.
// Before: "******" passed through → "Authorization: ****** sk-or-v1-xxx"
// After:
const strip = (v) => ((v || '').replace(/^\s*Bearer\s+/i, '').trim()) || undefined;
return strip(env.COPILOT_GITHUB_TOKEN) || strip(env.COPILOT_API_KEY);

containers/api-proxy/server.js

  • proxyRequest: Extend the upstream_auth_error log event to include HTTP 400 (alongside 401/403). A 400 from an external BYOK provider is a strong signal of a malformed auth header; the new message calls out the "Bearer " prefix explicitly so operators can diagnose without exhausting retries.

containers/api-proxy/server.test.js

  • New describe('createCopilotAdapter — BYOK getAuthHeaders') suite covering correct header format, double-prefix prevention, and edge cases.
  • Extended resolveCopilotAuthToken tests for Bearer-prefix stripping (case variants, prefix-only input).
  • Three createProviderServer tests asserting upstream_auth_error fires on 400/401 but not 200.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.example.com
    • Triggering command: /opt/hostedtoolcache/node/20.20.2/x64/bin/node node /home/REDACTED/work/gh-aw-firewall/gh-aw-firewall/containers/api-proxy/node_modules/.bin/jest --testPathPatterns=server.test --verbose (dns block)
    • Triggering command: /opt/hostedtoolcache/node/20.20.2/x64/bin/node node /home/REDACTED/work/gh-aw-firewall/gh-aw-firewall/containers/api-proxy/node_modules/.bin/jest --testPathPatterns=server.test --verbose s/.bin/jest (dns block)
    • Triggering command: /opt/hostedtoolcache/node/20.20.2/x64/bin/node /opt/hostedtoolcache/node/20.20.2/x64/bin/node /home/REDACTED/work/gh-aw-firewall/gh-aw-firewall/containers/api-proxy/node_modules/jest-worker/build/processChild.js (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

- Strip accidental "Bearer " prefix from COPILOT_API_KEY/COPILOT_GITHUB_TOKEN
  in resolveCopilotAuthToken() and createCopilotAdapter() to prevent
  double-prefixed "Authorization: Bearer Bearer <key>" rejected by external
  providers (e.g. OpenRouter) with 400 "badly formatted" error
- Extend upstream_auth_error log to also fire on HTTP 400 with BYOK hint
- Add unit tests: Bearer-prefix stripping, BYOK getAuthHeaders format,
  400/401 upstream_auth_error log events

Agent-Logs-Url: https://github.com/github/gh-aw-firewall/sessions/f4fac661-5248-46ac-919a-4f77d41f89a0
Copilot AI changed the title [WIP] Fix badly formatted Authorization header in api-proxy for BYOK mode fix(api-proxy): strip accidental "Bearer " prefix in BYOK Copilot auth token May 4, 2026
Copilot AI requested a review from lpcox May 4, 2026 23:17
Copilot finished work on behalf of lpcox May 4, 2026 23:17
@lpcox lpcox marked this pull request as ready for review May 4, 2026 23:26
@lpcox lpcox requested a review from Mossaka as a code owner May 4, 2026 23:26
Copilot AI review requested due to automatic review settings May 4, 2026 23:26
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

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 hardens the api-proxy Copilot BYOK auth path by sanitizing accidentally-prefixed Bearer tokens before header injection, and improves operator diagnostics by treating upstream HTTP 400 as a likely auth-formatting problem.

Changes:

  • Strip an accidental Bearer prefix (case-insensitive, tolerating leading whitespace) from COPILOT_GITHUB_TOKEN / COPILOT_API_KEY before building Copilot auth headers.
  • Extend upstream_auth_error logging to include upstream HTTP 400 with a more prescriptive message.
  • Add/extend Jest coverage for token stripping, BYOK header formatting, and upstream auth-error logging on 400/401 (and not on 200).
Show a summary per file
File Description
containers/api-proxy/providers/copilot.js Sanitize env-derived Copilot tokens to prevent double-Bearer Authorization headers.
containers/api-proxy/server.js Emit upstream_auth_error for upstream HTTP 400 in addition to 401/403, with a targeted diagnostic message.
containers/api-proxy/server.test.js Add regression tests for Bearer-prefix stripping, BYOK header injection, and 400/401 auth-error logging behavior.

Copilot's findings

Tip

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

  • Files reviewed: 3/3 changed files
  • Comments generated: 3

Comment on lines +382 to 390
if (proxyRes.statusCode === 400 || proxyRes.statusCode === 401 || proxyRes.statusCode === 403) {
const message = proxyRes.statusCode === 400
? `Upstream returned 400 — possible malformed Authorization header; check that the API key does not include a "Bearer " prefix (BYOK mode)`
: `Upstream returned ${proxyRes.statusCode} — check that the API key is valid and has not expired`;
logRequest('warn', 'upstream_auth_error', {
request_id: requestId, provider, status: proxyRes.statusCode,
upstream_host: targetHost, path: sanitizeForLog(req.url),
message: `Upstream returned ${proxyRes.statusCode} — check that the API key is valid and has not expired`,
message,
});
Comment thread containers/api-proxy/server.test.js Outdated
Comment on lines +965 to +968
// These tests guard against the "badly formatted Authorization header" bug in
// BYOK mode (COPILOT_PROVIDER_API_KEY + COPILOT_PROVIDER_BASE_URL) where the
// sidecar could produce "Authorization: Bearer Bearer <key>" if the key value
// already contained the "Bearer " prefix. They also verify that the header
Comment on lines 32 to 38
function resolveCopilotAuthToken(env = process.env) {
const githubToken = (env.COPILOT_GITHUB_TOKEN || '').trim() || undefined;
const apiKey = (env.COPILOT_API_KEY || '').trim() || undefined;
return githubToken || apiKey;
// Strip any accidental "Bearer " prefix (with optional leading whitespace) before
// trimming, so that a value like "Bearer " (prefix only, no actual token)
// correctly reduces to undefined rather than "Bearer".
const strip = (v) => ((v || '').replace(/^\s*Bearer\s+/i, '').trim()) || undefined;
return strip(env.COPILOT_GITHUB_TOKEN) || strip(env.COPILOT_API_KEY);
}
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@lpcox
Copy link
Copy Markdown
Collaborator

lpcox commented May 4, 2026

@copilot address the review feedback

@github-actions

This comment has been minimized.

- Extract stripBearerPrefix() as a named module-level helper in
  copilot.js, reused by both resolveCopilotAuthToken() and
  createCopilotAdapter() — eliminates the duplicated inline `strip`
  function; export for direct unit testing
- Replace Copilot/BYOK-specific HTTP 400 message in proxyRequest with
  a generic "check that the API key is valid and correctly formatted"
  message that is accurate for all providers
- Fix server.test.js BYOK mode comment to reference COPILOT_API_KEY
  (the sidecar's actual input) rather than COPILOT_PROVIDER_API_KEY
  (the agent-side placeholder)
- Add dedicated describe('stripBearerPrefix') unit tests for the new
  helper; simplify resolveCopilotAuthToken integration tests

Agent-Logs-Url: https://github.com/github/gh-aw-firewall/sessions/fcc4060e-94ff-474f-8254-d8b81acd6204

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 4, 2026

@copilot address the review feedback

Addressed all three review points in 8a55685:

  1. Duplicated strip function — extracted a single stripBearerPrefix(value) module-level helper in copilot.js, reused by both resolveCopilotAuthToken and createCopilotAdapter. Exported for direct unit testing with a new describe('stripBearerPrefix') suite.

  2. BYOK-specific 400 message in generic proxyRequest — replaced with the neutral "check that the API key is valid and correctly formatted" message that applies equally to all providers.

  3. Incorrect BYOK comment in tests — updated to reference COPILOT_API_KEY (the sidecar's actual credential env var) rather than COPILOT_PROVIDER_API_KEY (the agent-side placeholder).

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

🔥 Smoke Test Results — PR #2521

Test Result
GitHub MCP connectivity
github.com HTTP ✅ 200
File write/read ❌ file not found (path not substituted)

Overall: FAIL (file test inconclusive — template vars not expanded)

PR: fix(api-proxy): strip accidental "Bearer " prefix in BYOK Copilot auth token
Author: @Copilot | Assignees: @lpcox, @Copilot

📰 BREAKING: Report filed by Smoke Copilot

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

🔥 Smoke Test: Copilot BYOK (Offline Mode)

Test Result
GitHub MCP connectivity ✅ PR listed successfully
GitHub.com connectivity ✅ Reachable
File write/read /tmp/gh-aw/agent/smoke-test-copilot-byok-25349480211.txt verified
BYOK inference (api-proxy → api.githubcopilot.com) ✅ Responding

Running in BYOK offline mode (COPILOT_OFFLINE=true) via api-proxy → api.githubcopilot.com

Overall: PASS — PR by @Copilot, assignees: @lpcox, @Copilot

🔑 BYOK report filed by Smoke Copilot BYOK

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

Smoke Test Results:
✅ GitHub MCP: PR #2509, #2501
✅ Playwright: github.com loaded (title contains "GitHub")
✅ File Writing: /tmp/gh-aw/agent/smoke-test-claude-25349480234.txt
✅ Bash Tool: File verified

Status: PASS

💥 [THE END] — Illustrated by Smoke Claude

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

Chroot Smoke Test Results

Runtime Host Version Chroot Version Match?
Python Python 3.12.13 Python 3.12.3
Node.js v24.14.1 v20.20.2
Go go1.22.12 go1.22.12

Result: ❌ Not all versions matched — Python and Node.js versions differ between host and chroot environments.

Tested by Smoke Chroot

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

PRs: fix: remove unused exports from public API surface; refactor: split docker-manager.ts into focused modules
GitHub MCP review ❌ | safeinputs-gh query ❌
Playwright title check ✅ | Tavily search ❌
File write ✅ | Bash cat ✅
Discussion interaction ❌
Build AWF ✅
Overall: FAIL

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • registry.npmjs.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "registry.npmjs.org"

See Network Configuration for more information.

🔮 The oracle has spoken through Smoke Codex

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

🏗️ Build Test Suite Results

Ecosystem Project Build/Install Tests Status
Bun elysia 1/1 passed ✅ PASS
Bun hono 1/1 passed ✅ PASS
C++ fmt N/A ✅ PASS
C++ json N/A ✅ PASS
Deno oak N/A 1/1 passed ✅ PASS
Deno std N/A 1/1 passed ✅ PASS
.NET hello-world N/A ✅ PASS
.NET json-parse N/A ✅ PASS
Go color 1/1 passed ✅ PASS
Go env 1/1 passed ✅ PASS
Go uuid 1/1 passed ✅ PASS
Java gson 1/1 passed ✅ PASS
Java caffeine 1/1 passed ✅ PASS
Node.js clsx passed ✅ PASS
Node.js execa passed ✅ PASS
Node.js p-limit passed ✅ PASS
Rust fd 1/1 passed ✅ PASS
Rust zoxide 1/1 passed ✅ PASS

Overall: 8/8 ecosystems passed — ✅ PASS

Generated by Build Test Suite for issue #2521 · ● 524K ·

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

Smoke Test Results — FAIL

  • Redis PING: ❌ (timeout, no response)
  • PostgreSQL pg_isready: ❌ (no response)
  • PostgreSQL SELECT 1: ❌ (not attempted, host unreachable)

Overall: FAILhost.docker.internal service containers are not reachable from this environment.

🔌 Service connectivity validated by Smoke Services

@lpcox lpcox merged commit 899ee4b into main May 4, 2026
64 of 68 checks passed
@lpcox lpcox deleted the copilot/fix-badly-formatted-authorization-header branch May 4, 2026 23:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[awf] api-proxy: BYOK Authorization header badly formatted when forwarding to external provider

3 participants