fix(api-proxy): use 'token' auth prefix for Copilot Business endpoint#5415
Conversation
The Copilot auth-prefix logic only applied the 'token <oauth>' Authorization prefix when the resolved target was api.enterprise.githubcopilot.com (via isGhesInstance). Copilot Business customers set COPILOT_API_TARGET=api.business.githubcopilot.com, often on a *.ghe.com (GHEC) server. For that combination isGhesInstance returns false, so the GitHub token went out as 'Bearer <oauth>' and the business endpoint rejected it with "400 bad request: Authorization header is badly formatted". Add copilotTargetRequiresGitHubTokenPrefix(), which applies the 'token' prefix for both the Enterprise and Business GitHub-hosted Copilot endpoints (in addition to GHES), while still honoring an explicit non-GHES AWF_PLATFORM_TYPE override and keeping 'Bearer' for BYOK keys and standard/GHEC targets. Fixes github/gh-aw#38575 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
✅ Coverage Check PassedOverall Coverage
📁 Per-file Coverage Changes (1 files)
Coverage comparison generated by |
There was a problem hiding this comment.
Pull request overview
Fixes Copilot Business requests failing with 400 ... Authorization header is badly formatted by ensuring GitHub OAuth/PAT credentials use the correct token <value> auth scheme when targeting GitHub-hosted Copilot endpoints that require it (Business + Enterprise), including the /models path and inference calls.
Changes:
- Introduces
copilotTargetRequiresGitHubTokenPrefix()to decide when GitHub tokens must be sent asAuthorization: token <value>. - Updates the Copilot provider adapter to apply
tokenvsBearerconsistently for/modelsand inference based on the new predicate. - Adds unit + adapter-level tests covering Business host scenarios (including Business on
*.ghe.com) andAWF_PLATFORM_TYPEoverride behavior.
Show a summary per file
| File | Description |
|---|---|
| containers/api-proxy/providers/copilot.js | Switches auth-prefix decision logic to the new predicate for /models and inference requests. |
| containers/api-proxy/providers/copilot-auth.js | Adds copilotTargetRequiresGitHubTokenPrefix() and target allowlist for hosts requiring token prefix for GitHub credentials. |
| containers/api-proxy/copilot-auth.test.js | Adds focused unit tests for the new predicate across Business/Enterprise/GHES/GHEC/override cases. |
| containers/api-proxy/copilot-adapter-enterprise.test.js | Adds adapter-level regression tests ensuring Business target uses token, while BYOK remains Bearer. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 4/4 changed files
- Comments generated: 0
|
Chroot tests passed! Smoke Chroot - All security and functionality tests succeeded. |
|
❌ Smoke Copilot BYOK AOAI (api-key) reports failed. AOAI BYOK (api-key) mode investigation needed... |
|
✅ Smoke Copilot BYOK completed. Copilot BYOK mode operational. 🔓 |
|
✅ Build Test Suite completed successfully! |
|
🔌 Smoke Services — All services reachable! ✅ |
|
✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟 |
|
✅ Smoke Claude passed |
|
🔑 Smoke Copilot PAT PAT auth validated. All systems operational. ✅ |
|
✅ Smoke Gemini completed. All facets verified. 💎 |
|
📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤 |
|
📡 Smoke OTel Tracing completed. All tracing scenarios validated. ✅ |
|
✅ Smoke Copilot BYOK AOAI (Entra) completed. Copilot AOAI BYOK (Entra) mode operational. 🔓 |
Smoke Test: Claude Engine Validation
Overall result: PASS
|
🔥 Smoke Test: Copilot PAT — PASS
Overall: PASS — @lpcox Auth mode: PAT (COPILOT_GITHUB_TOKEN)
|
🔥 Smoke Test ResultsPR: fix(api-proxy): use 'token' auth prefix for Copilot Business endpoint
Overall: PASS
|
|
✅ Smoke Test: Copilot BYOK (Direct Mode) - PASS
Running in direct BYOK mode (COPILOT_PROVIDER_API_KEY) with api-proxy forwarding.
|
|
Remove unused Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "registry.npmjs.org"See Network Configuration for more information.
|
Chroot Version Comparison Results
Overall: ❌ Not all tests passed. Python and Node.js versions differ between host and chroot environments.
|
Running in direct BYOK mode (AWF_AUTH_TYPE=github-oidc + AWF_AUTH_AZURE_* + COPILOT_PROVIDER_BASE_URL) via api-proxy → Azure OpenAI (Foundry, o4-mini-aw) authenticated via Microsoft Entra
|
|
Smoke test results:
Running in direct BYOK mode (COPILOT_PROVIDER_API_KEY + COPILOT_PROVIDER_BASE_URL) via api-proxy → Azure OpenAI (Foundry, o4-mini-aw) Overall: PASS
|
🔭 Smoke Test: API Proxy OpenTelemetry Tracing
All scenarios pass. OTEL tracing integration is fully operational.
|
🏗️ Build Test Suite Results
Overall: 8/8 ecosystems passed — ✅ PASS
|
Smoke Test Results: FAIL
Overall: FAIL —
|
|
GitHub MCP Testing: ❌ (MCP tools not available) Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "localhost"See Network Configuration for more information.
|
Problem
Copilot Business customers (
COPILOT_API_TARGET=api.business.githubcopilot.com, typically on a*.ghe.comGHEC server) get400 bad request: Authorization header is badly formattedon every Copilot request, and the run exits 1.The auth-prefix logic in
copilot.js#getAuthHeadersonly applied thetoken <oauth>Authorization prefix whenisGhesInstance(rawTarget, env)was true.isGhesInstanceonly matches the enterprise host (api.enterprise.githubcopilot.com) directly, and itsGITHUB_SERVER_URLheuristic explicitly treats*.ghe.comas not GHES. So for the business host on a*.ghe.comserver it returnsfalse, the GitHub OAuth token goes out asBearer <oauth>, and the business endpoint rejects it.api.business.githubcopilot.comdid not appear anywhere in the source. The prior fixes (#4755, #5076) covered enterprise/GHES only.Reported in github/gh-aw#38575 (still reproducing as of 2026-06-19).
Fix
Add
copilotTargetRequiresGitHubTokenPrefix(resolvedTarget, env)incopilot-auth.js, and use it ingetAuthHeadersfor both the/modelspath and inference requests.It returns
true(→tokenprefix for GitHub tokens) when either:isGhesInstance()is true (unchanged GHES behavior), orapi.enterprise.githubcopilot.comorapi.business.githubcopilot.com.Preserved invariants:
AWF_PLATFORM_TYPEstill forcesBearer(documented escape hatch, consistent withisGhesInstance).Bearer.api.githubcopilot.comand GHEC (*.ghe.com) Copilot targets keepBearer.Tests
copilot-auth.test.js: 8 unit tests forcopilotTargetRequiresGitHubTokenPrefix(business host, business on*.ghe.com, GHES, standard, GHEC,AWF_PLATFORM_TYPEoverride).copilot-adapter-enterprise.test.js: 5 adapter-level tests for the Business host (inference +/modelsusetoken; BYOK staysBearer; accidentaltokenprefix stripped).Fixes github/gh-aw#38575
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com