Skip to content

fix(oauth): GitHub Copilot token refresh sends the public client_id - #4320

Merged
diegosouzapw merged 1 commit into
release/v3.8.30from
fix/port-issue-442-github-clientid-reauth
Jun 20, 2026
Merged

diegosouzapw merged 1 commit into
release/v3.8.30from
fix/port-issue-442-github-clientid-reauth

Conversation

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Summary

  • The github provider config never carried a clientId, so GitHub OAuth refresh_token exchanges were malformed and rejected.
  • A Copilot connection using the long-lived refresh path (after its short-lived token expired) got stuck.
  • The provider now resolves its public device-flow client_id from the embedded public credential, and client_secret is only sent when one actually exists.

Root cause

PROVIDERS.github had no clientId (the registry entry had authType: "oauth" but no oauth block, so generateLegacyProviders never set it). The two refresh paths read it:

  • open-sse/services/tokenRefresh.ts → buildFormParams drops undefined values, so client_id was omitted entirely.
  • open-sse/executors/github.ts → new URLSearchParams({...}) stringified undefined, sending the literal client_id=undefined&client_secret=undefined.

Both are rejected by GitHub's token endpoint.

Fix

  • Add an oauth block to the github registry entry that resolves clientId from the embedded public credential via resolvePublicCred("github_copilot_id") (Hard Rule chore(ui): rebrand to OmniRoute #11 — never a string literal; env override via GITHUB_OAUTH_CLIENT_ID).
  • The executor now builds the form params with the real client_id and only attaches client_secret when one is configured (GitHub Copilot is a public client — it has none).

Attribution

Thanks to @baslr for the original fix.

Test plan

  • tests/unit/token-refresh-service.test.ts — rewrote the github refresh test to drop the masking patch and assert the real resolved client_id is sent with no client_secret (red before, green after); 37/37 pass
  • tests/unit/executor-github.test.ts — new test asserting the executor sends the real client_id and never client_secret=undefined (red before, green after); 12/12 pass
  • npm run typecheck:core — clean
  • eslint on changed files — 0 errors
  • error-classification + check-public-creds suites — green

…9router#442)

GitHub Copilot is a public device-flow OAuth client (client_id, no client_secret),
but the github provider config never populated clientId. The standalone refresh path
omitted client_id (buildFormParams drops undefined) and the executor path sent the
literal "client_id=undefined&client_secret=undefined" — both rejected by GitHub, so a
Copilot connection got stuck once its short-lived token expired and the long-lived
refresh path was needed. Populate the provider clientId from the embedded public cred
(resolvePublicCred, never a literal) and only send client_secret when one exists. The
prior github refresh test patched a fake clientId/clientSecret onto PROVIDERS.github,
masking the broken real config — it now exercises the real config.

Co-authored-by: Manuel B. <1494154+baslr@users.noreply.github.com>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@diegosouzapw
diegosouzapw merged commit 0323874 into release/v3.8.30 Jun 20, 2026
4 checks passed
@diegosouzapw diegosouzapw mentioned this pull request Jun 20, 2026
@diegosouzapw
diegosouzapw deleted the fix/port-issue-442-github-clientid-reauth branch June 20, 2026 19:27
tkgo11 pushed a commit to tkgo11/OmniRoute that referenced this pull request Sep 23, 2026
…heaper-inference#4320)

GitHub Copilot is a public device-flow OAuth client (client_id, no client_secret),
but the github provider config never populated clientId. The standalone refresh path
omitted client_id (buildFormParams drops undefined) and the executor path sent the
literal "client_id=undefined&client_secret=undefined" — both rejected by GitHub, so a
Copilot connection got stuck once its short-lived token expired and the long-lived
refresh path was needed. Populate the provider clientId from the embedded public cred
(resolvePublicCred, never a literal) and only send client_secret when one exists. The
prior github refresh test patched a fake clientId/clientSecret onto PROVIDERS.github,
masking the broken real config — it now exercises the real config.

Co-authored-by: Manuel B. <1494154+baslr@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant