Validate OAuth-issued provider tokens carry API scope before onboarding completes - #574
Merged
TheGreatAxios merged 2 commits intoAug 23, 2026
Conversation
…ng completes A completed Codex/xAI OAuth login proves the token is real but not that it carries usable API scope (e.g. a chat-only subscription). Probe each provider's own catalog endpoint with the issued token before treating onboarding as complete: a definitive 401/403 rejects the submit with an actionable message pointing back to reconnecting; a check that can't run at all (network blip, timeout, 5xx) never blocks onboarding, only a proven scope failure does. Neither the token nor any response body is logged or persisted.
TheGreatAxios
enabled auto-merge
August 23, 2026 20:24
mock.module for codex/session.js and xai/session.js in oauth-scope-check.test.ts, and for oauth-scope-check.js in provider-setup-submit.test.ts, replaced those modules for the whole bun test process without restoring them, breaking tests/unit/codex-session.test.ts which imports the real module directly. Capture the real module before mocking and restore it in afterAll. Also drops an unused beforeEach import that eslint flagged.
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.
Closes CL-5710.
Problem
The OAuth submit path (
src/tui/provider-setup-submit.ts) treated onboarding as complete on any successful Codex/xAI login. A completed OAuth login proves the token is real (issued by the provider's own authorization server via a PKCE round-trip), but not that the token carries usable API scope — e.g. a chat-only subscription without API access. That gap surfaced as a confusing first-send auth error with no setup-attributable hint, the same failure shape CL-5687/5688/5689/5690 fixed for the API-key path (PR #405).Confirmed still present on main: the
oauthbranch inbuildProviderSubmitHandlerpersisteddefaultProviderand the local selection unconditionally, with a comment explicitly noting the scope gap was "tracked separately."Fix
Added
src/auth/oauth-scope-check.ts, which probes the provider's own catalog/list endpoint with the token that was just issued — the same authoritative surface a real inference call would hit — rather than trusting any scope string the issuer echoes back:GET /codex/modelsonchatgpt.com/backend-apiwith the same auth headersfetchCodexModels/fetchCodexUsagealready use.GET /modelson the CLI chat proxy (cli-chat-proxy.grok.com/v1) with the same auth headersfetchXaiUsagealready uses.The result is classified into three states, and
buildProviderSubmitHandler's oauth branch only blocks on one of them:ok— proceed, onboarding completes normally.insufficient-scope(a definitive 401/403) — the submit is rejected with an actionable message telling the user their sign-in lacks API access and to reconnect, instead of a raw adapter error. Nothing is persisted.unavailable(network error, timeout, 429/5xx, or anything else inconclusive) — onboarding proceeds. A transient failure must never lock a legitimate user out of onboarding; only a proven scope failure blocks.skipValidation(the existing Ctrl+S bypass) skips the probe entirely, matching the API-key path's behavior.Neither the token nor any response body is logged or persisted — only the HTTP status is inspected.
Tests
src/auth/oauth-scope-check.test.ts— codex/xai probes: valid scope (200), definitive insufficient scope (401/403), and check-unavailable (network throw, timeout, 500).src/tui/provider-setup-submit.test.ts— newdescribeblock covering the same three paths throughbuildProviderSubmitHandler's oauth branch, plus askipValidationbypass case.bunx tsc --noEmitis clean; affected suites (src/auth/**,src/tui/provider-setup-submit.test.ts,src/tui/provider-setup.test.ts,src/tui/runner.test.ts) pass.