Repository navigation
fix(oauth): GitHub Copilot token refresh sends the public client_id - #4320
Merged
diegosouzapw merged 1 commit intoJun 20, 2026
Merged
Conversation
…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>
Contributor
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Merged
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
githubprovider config never carried aclientId, so GitHub OAuthrefresh_tokenexchanges were malformed and rejected.client_idfrom the embedded public credential, andclient_secretis only sent when one actually exists.Root cause
PROVIDERS.githubhad noclientId(the registry entry hadauthType: "oauth"but nooauthblock, sogenerateLegacyProvidersnever set it). The two refresh paths read it:open-sse/services/tokenRefresh.ts→buildFormParamsdrops undefined values, soclient_idwas omitted entirely.open-sse/executors/github.ts→new URLSearchParams({...})stringified undefined, sending the literalclient_id=undefined&client_secret=undefined.Both are rejected by GitHub's token endpoint.
Fix
oauthblock to the github registry entry that resolvesclientIdfrom the embedded public credential viaresolvePublicCred("github_copilot_id")(Hard Rule chore(ui): rebrand to OmniRoute #11 — never a string literal; env override viaGITHUB_OAUTH_CLIENT_ID).client_idand only attachesclient_secretwhen 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 resolvedclient_idis sent with noclient_secret(red before, green after); 37/37 passtests/unit/executor-github.test.ts— new test asserting the executor sends the realclient_idand neverclient_secret=undefined(red before, green after); 12/12 passnpm run typecheck:core— cleaneslinton changed files — 0 errorserror-classification+check-public-credssuites — green