fix(copilot): switch to VS Code client ID and derive enterprise base URL#15139
fix(copilot): switch to VS Code client ID and derive enterprise base URL#15139difujia wants to merge 2 commits into
Conversation
e25098f to
a1cdc27
Compare
|
@teknium1 Heads up — this PR is the final piece of the Copilot auth work you cherry-picked in 7632919 and d7ad07d. Without this change, the token exchange you landed in d7ad07d is actually non-functional: the current opencode client ID ( This PR also adds enterprise endpoint derivation from the See the test matrix in the PR description for the full Individual × Enterprise × Client ID comparison. |
a1cdc27 to
15e46c2
Compare
|
Confirming this fix works on a Copilot Business seat. Applied locally ( |
15e46c2 to
3d78a5c
Compare
|
Pushed a follow-up commit ( Symptom (May 2026): Enterprise Copilot users hit 400 errors:
Root cause: ~15 host-matching call sites still hard-coded Fix: Broadened all checks to Tests: 2 new (enterprise endpoint preserves |
|
Small process note for visibility: I tried to reopen #7731 since parts 3 and 4 from @konsisumer's original scoping pass were never delivered (and part 2 — token exchange — shipped in d7ad07d but is non-functional on every account type without the part 3 client ID swap, as confirmed by @lucvan on Business and cross-referenced for Individual in #16551). GitHub returned 422 because only maintainers can reopen issues closed as |
Two changes that complete the Copilot auth story (NousResearch#7731 parts 3 and 4): 1. Switch OAuth client ID from opencode (Ov23li8tweQw6odWQebz) to VS Code (Iv1.b507a08c87ecfe98). The old ID produces gho_* tokens that return 404 on /copilot_internal/v2/token, making token exchange non-functional. The new ID produces ghu_* tokens that support exchange. 2. Derive enterprise API base URL from the proxy-ep field in the exchanged token. Enterprise accounts get tokens containing e.g. "proxy-ep=proxy.enterprise.githubcopilot.com" which is converted to "https://api.enterprise.githubcopilot.com" and stored in the credential pool. Individual accounts (no proxy-ep) continue using the default URL. The COPILOT_API_BASE_URL env var remains as a user escape hatch. Tested on both Individual and Enterprise Copilot accounts: - Individual: device flow works, exchange succeeds, base_url=None (default) - Enterprise: device flow works, exchange succeeds, 39 models returned including claude-opus-4.6-1m (936K), enterprise base URL derived Parts 3 and 4 of NousResearch#7731.
The earlier enterprise base URL change (proxy-ep parsing) gave us URLs
like `api.enterprise.githubcopilot.com`, but ~15 host-matching call
sites still hard-coded `api.githubcopilot.com`. Enterprise users would
therefore drop the `Copilot-Integration-Id: vscode-chat` header at
client-build time, and upstream rejected requests with:
The requested model is not available for integrator "zed"
(or "copilot-language-server") — verify the correct
Copilot-Integration-Id header is being sent.
The header was correct in copilot_default_headers(); it just never
made it into default_headers for non-default hostnames because every
detector compared against the exact string "api.githubcopilot.com".
This commit broadens all those checks to "githubcopilot.com" via
base_url_host_matches (which already does proper subdomain matching),
so api.enterprise.githubcopilot.com, api.business.githubcopilot.com,
etc. all share the same headers, vision routing, max_completion_tokens
selection, and reasoning-effort detection as the default endpoint.
Also adds ".githubcopilot.com" to _URL_TO_PROVIDER so context-window
resolution via models.dev works for enterprise base URLs, and tightens
_is_github_copilot_url to use suffix matching instead of strict equality.
Tests:
- New: enterprise Copilot endpoint preserves Copilot-Integration-Id
- New: enterprise endpoint returns max_completion_tokens (not max_tokens)
- Existing 333 base_url / copilot / aux-client / credential-pool tests pass
Parts 5 of NousResearch#7731.
1695140 to
2dad14d
Compare
Fixes #6455
Refs #7731 (parts 3 and 4), completing the Copilot auth improvements.
Changes
Part 3: Switch OAuth client ID
The opencode OAuth App ID (
Ov23li8tweQw6odWQebz) producesgho_*tokens that return 404 on/copilot_internal/v2/token, making the token exchange (landed in d7ad07d) non-functional. VS Code's GitHub App ID (Iv1.b507a08c87ecfe98) producesghu_*tokens that support exchange.One-line change:
COPILOT_OAUTH_CLIENT_IDinhermes_cli/copilot_auth.py.Part 4: Derive enterprise base URL from proxy-ep
Enterprise Copilot accounts get exchanged tokens containing a
proxy-epfield (e.g.proxy-ep=proxy.enterprise.githubcopilot.com). This is now parsed and converted to an API base URL (https://api.enterprise.githubcopilot.com) which is stored in the credential pool.exchange_copilot_token()now returns(api_token, expires_at, base_url)get_copilot_api_token()now returns(api_token, base_url)credential_pool.pyuses the enterprise base URL when available, falls back to defaultCOPILOT_API_BASE_URLenv var still works as a user escape hatchTest matrix (Individual × Enterprise × Client ID)
Ov23li...gho_*Iv1.b5...ghu_*Ov23li...gho_*Iv1.b5...ghu_*¹ The Individual account tested has an Enterprise Copilot seat linked, so its model catalog matches Enterprise (both show 39 models after exchange). A pure Individual account without Enterprise linkage may see fewer models — the important signal is that device flow and token exchange work correctly on both account types.
Key findings:
gho_*tokens (opencode ID) never support exchange, regardless of account typeghu_*tokens (VS Code ID) support exchange on both Individual and Enterpriseclaude-opus-4.6-1m(936K prompt tokens)gpt-4.1context jumps from 64K → 128K after exchangeproxy-epfor base URL derivation; Individual tokens do not216 existing tests pass. 6 new tests for proxy-ep parsing and enterprise base URL derivation.
Note: Existing users will need to re-run
copilot login/hermes modelto get a newghu_*token.