Skip to content

GitHub Copilot: own GitHub App rejected by /copilot_internal/v2/token; now impersonating Neovim OAuth App #1157

@Aaronontheweb

Description

@Aaronontheweb

Summary

The Netclaw GitHub App (Iv23lipIurKdMkbqy6nH, registered as netclaw-dev) returns HTTP 403 "Resource not accessible by integration" from https://api.github.com/copilot_internal/v2/token on every exchange attempt, regardless of which permissions the App declares. As of this issue's fix we work around it by impersonating the Neovim Copilot plugin's OAuth App (Iv1.b507a08c87ecfe98) — the same posture every other community Copilot client takes (avante.nvim, copilot.lua, CodeAlta). This issue captures the diagnosis so we can revisit the integration model later.

Diagnosis

Stage 0 of PR #1075 used the Neovim plugin's OAuth App ID as a placeholder. Commit b3d64d3f swapped it out for a Netclaw-owned GitHub App registered with "Account email read-only" permission. That swap broke any newly-minted OAuth token because:

  • /copilot_internal/v2/token is the editor-integration surface (VS Code, Neovim, JetBrains, gh CLI). It rejects GitHub App user-to-server tokens regardless of permission claims.
  • Adding Copilot Chat: Read-only and Copilot Editor Context: Read-only to the App did not change the behavior — still 403.
  • A clean Docker container, fresh device flow, fresh exchange request: still 403.

Empirically verified across:

  • The Netclaw-owned App at multiple permission settings
  • A fresh container (netclaw-copilot-test) with no prior state
  • A CodeAlta-style reference implementation in CodeAlta/CodeAlta — comment in their source: "GitHub Copilot extension OAuth app id. Ideally we should use our own, but for now we do what others do."

What was actually shipped (workaround)

  • Reverted ClientId in src/Netclaw.Providers/GitHubCopilot/GitHubCopilotDescriptor.cs to Iv1.b507a08c87ecfe98 (Neovim Copilot plugin OAuth App, allowlisted).
  • Added the editor-integration header contract on the exchange request (Editor-Version, Editor-Plugin-Version, Copilot-Integration-Id: vscode-chat, X-GitHub-Api-Version) — without Copilot-Integration-Id GitHub's gateway routes the token through the generic App permission model and the 403 stands even with the right client ID.
  • Switched the exchange Authorization scheme from token to Bearer to match what working clients send.

Real options to revisit

  1. Apply to GitHub for Netclaw's own OAuth App (not GitHub App) to be added to the Copilot integration allowlist. Clean long-term answer. Wall-clock measured in weeks; requires a partner-integration review on GitHub's side.
  2. Migrate to the documented Copilot SDK pathway (docs.github.com/en/copilot/how-tos/copilot-sdk/...). The SDK uses the OAuth token directly, both OAuth Apps and GitHub Apps are first-class, and we'd be off the _internal endpoint permanently. A real engineering project — would be a v2 of the Copilot provider.
  3. Stay on impersonation indefinitely. Grey but proven posture. Works today.

Related: #1148 — GitHub Enterprise support

Almost certainly the same root cause. Two things bind us to non-enterprise github.com today:

  • The hardcoded https://api.github.com/copilot_internal/v2/token endpoint in src/Netclaw.Providers/GitHubCopilot/CopilotTokenExchanger.cs:32-33. Enterprise tenants use per-org subdomains under *.ghe.com; the exchange endpoint shape differs.
  • The hardcoded https://api.githubcopilot.com base for chat in src/Netclaw.Providers/GitHubCopilot/GitHubCopilotDescriptor.cs:24. CodeAlta's implementation resolves this dynamically from the exchange response's endpoints.api field AND from a proxy-ep= marker embedded in the Copilot token string itself, defaulting to api.individual.githubcopilot.com for personal accounts. See CodeAlta CopilotDirectAuth.cs#L407-L465.

Both of these need to land together for #1148 to be tractable; pursuing the SDK migration (option 2 above) would close both this issue and that one.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    providersProvider integrations and capability detection across OpenAI-compatible backends.

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions