Skip to content

fix(copilot): switch to VS Code client ID and derive enterprise base URL#15139

Open
difujia wants to merge 2 commits into
NousResearch:mainfrom
difujia:fix/copilot-oauth-client-id
Open

fix(copilot): switch to VS Code client ID and derive enterprise base URL#15139
difujia wants to merge 2 commits into
NousResearch:mainfrom
difujia:fix/copilot-oauth-client-id

Conversation

@difujia

@difujia difujia commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

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) produces gho_* 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) produces ghu_* tokens that support exchange.

One-line change: COPILOT_OAUTH_CLIENT_ID in hermes_cli/copilot_auth.py.

Part 4: Derive enterprise base URL from proxy-ep

Enterprise Copilot accounts get exchanged tokens containing a proxy-ep field (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.py uses the enterprise base URL when available, falls back to default
  • COPILOT_API_BASE_URL env var still works as a user escape hatch
  • Individual accounts (no proxy-ep) are unaffected

Test matrix (Individual × Enterprise × Client ID)

Account Client ID Token type Device flow Token exchange Models (raw) Models (JWT) Enterprise base URL
Individual¹ opencode Ov23li... gho_* ❌ 404 31 N/A N/A
Individual¹ VS Code Iv1.b5... ghu_* 31 39 None (individual)
Enterprise opencode Ov23li... gho_* ❌ 404 31 N/A N/A
Enterprise VS Code Iv1.b5... ghu_* 31 39 ✅ derived from proxy-ep

¹ 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 type
  • ghu_* tokens (VS Code ID) support exchange on both Individual and Enterprise
  • After exchange, 8 additional models appear including claude-opus-4.6-1m (936K prompt tokens)
  • gpt-4.1 context jumps from 64K → 128K after exchange
  • Enterprise tokens contain proxy-ep for base URL derivation; Individual tokens do not

216 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 model to get a new ghu_* token.

@difujia difujia force-pushed the fix/copilot-oauth-client-id branch from e25098f to a1cdc27 Compare April 24, 2026 12:58
@difujia difujia changed the title fix(copilot): switch OAuth client ID to VS Code GitHub App ID fix(copilot): switch to VS Code client ID and derive enterprise base URL Apr 24, 2026
@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/cli CLI entry point, hermes_cli/, setup wizard provider/copilot GitHub Copilot (ACP + Chat) area/auth Authentication, OAuth, credential pools labels Apr 24, 2026
@difujia

difujia commented Apr 25, 2026

Copy link
Copy Markdown
Contributor Author

@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 (Ov23li8tweQw6odWQebz) produces gho_* tokens that return 404 on /copilot_internal/v2/token. Only VS Code's client ID (Iv1.b507a08c87ecfe98) produces ghu_* tokens that can be exchanged.

This PR also adds enterprise endpoint derivation from the proxy-ep field in the exchanged token, which @konsisumer flagged in #7731 as the natural follow-up once token exchange landed.

See the test matrix in the PR description for the full Individual × Enterprise × Client ID comparison.

@lucvan

lucvan commented May 12, 2026

Copy link
Copy Markdown

Confirming this fix works on a Copilot Business seat. Applied locally (COPILOT_OAUTH_CLIENT_ID = "Iv1.b507a08c87ecfe98"); the /copilot_internal/v2/token exchange that was returning HTTP 404 with the previous client_id succeeds immediately after the swap, and the live /models endpoint returns the full Claude + GPT-5 catalog. The OpenCode client_id appears to fail for both Individual and Business subscriptions (cross-referencing #16551 where an Individual user reports the same 404), so the framing in this PR's title — universal switch — matches what we see in the wild.

@difujia

difujia commented May 22, 2026

Copy link
Copy Markdown
Contributor Author

Pushed a follow-up commit (1695140fa) addressing a regression that surfaced after the enterprise base URL derivation landed.

Symptom (May 2026): Enterprise Copilot users hit 400 errors:

The requested model is not available for integrator "zed" / "copilot-language-server" — verify the correct Copilot-Integration-Id header is being sent.

Root cause: ~15 host-matching call sites still hard-coded api.githubcopilot.com. Enterprise base URLs like api.enterprise.githubcopilot.com failed every check, so copilot_default_headers() (which includes Copilot-Integration-Id: vscode-chat) was never applied — upstream then saw the integrator default to zed/copilot-language-server and rejected the request.

Fix: Broadened all checks to githubcopilot.com via base_url_host_matches (proper subdomain matching), added .githubcopilot.com to _URL_TO_PROVIDER for context-window resolution, and tightened _is_github_copilot_url to use suffix matching.

Tests: 2 new (enterprise endpoint preserves Copilot-Integration-Id; enterprise endpoint returns max_completion_tokens). All 333 related tests pass.

@difujia

difujia commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

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 completed, so flagging here instead — happy if a maintainer wants to reopen #7731 for tracking, otherwise this PR is the single source of truth for the remaining work.

@teknium1 @alt-glitch

difujia added 2 commits June 8, 2026 08:09
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.
@difujia difujia force-pushed the fix/copilot-oauth-client-id branch from 1695140 to 2dad14d Compare June 8, 2026 08:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/cli CLI entry point, hermes_cli/, setup wizard P1 High — major feature broken, no workaround provider/copilot GitHub Copilot (ACP + Chat) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Add support for Github Copilot though Github Enterprise subscription

3 participants