fix: step up OAuth scopes for under-scoped tokens in non-interactive sessions#34
Merged
Conversation
…sessions `get_credential_for` had an asymmetry: when a stored token was missing a required scope it consulted `session_is_interactive()` and, with no TTY, returned a hard "missing required scope(s)" error instead of acquiring the scope. The no-token branch, by contrast, always ran the browser flow. So a command run from a non-TTY context (e.g. an agent harness or piped stdio) failed when it held an under-scoped token, but auto-authenticated once the token was cleared — surprising and inconsistent. Drop the interactivity gate from the under-scoped branch so it takes the same `reauthenticate(union)` path the no-token branch already uses. Missing scopes now always trigger step-up; the browser flow's 120s callback timeout bounds truly-headless hangs (identical to today's no-token behavior). The union (defaults ∪ granted ∪ required) is unchanged, so step-up still never narrows an existing grant. Simplify `plan_step_up` to Covered/Reauthenticate and remove the now-unused `MissingNonInteractive` variant, `session_is_interactive`, and `missing_scope_error`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes inconsistent OAuth scope acquisition behavior in PkceAuthProvider::get_credential_for by making “token present but under-scoped” follow the same reauthentication (browser) flow as the “no token” case, even when stdio is non-interactive.
Changes:
- Removed the non-interactive “fail fast” branch for under-scoped tokens and always performs step-up via
reauthenticatewith the union ofdefaults ∪ granted ∪ required. - Simplified
plan_step_upto a two-outcome decision (CoveredvsReauthenticate) and deleted the associated TTY detection and error helper. - Updated unit tests to reflect the simplified step-up behavior.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
jbrooks2-godaddy
approved these changes
Jun 29, 2026
jpage-godaddy
pushed a commit
that referenced
this pull request
Jun 29, 2026
🤖 I have created a release *beep* *boop* --- ## [0.3.4](cli-engine-v0.3.3...cli-engine-v0.3.4) (2026-06-29) ### Bug Fixes * change default credential store from Keyring to Auto ([#31](#31)) ([ccca021](ccca021)) * step up OAuth scopes for under-scoped tokens in non-interactive sessions ([#34](#34)) ([9b82ee0](9b82ee0)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
jpage-godaddy
added a commit
to godaddy/cli
that referenced
this pull request
Jun 29, 2026
cli-engine 0.3.4 makes OAuth scope step-up fire for under-scoped tokens in non-interactive sessions (godaddy/cli-engine#34). Before this, a command run without a TTY (agent harness, piped stdio) that held a token missing a required scope hard-errored with "missing required scope(s)" instead of launching the OAuth flow, while the same command auto-authenticated once the token was cleared. With the bump, `gddy domain suggest` / `domain purchase` and any scoped command now step up to acquire the missing scope regardless of interactivity. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.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.
Problem
PkceAuthProvider::get_credential_forhandled scope acquisition asymmetrically:session_is_interactive()(a TTY check). With no TTY → hard error:missing required scope(s): …; run auth login --env … --scope … in an interactive terminal.reauthenticate(browser flow) unconditionally, no interactivity gate.So a command run from a non-TTY context (agent harness, piped stdio, captured streams) failed when it held a token missing a scope, but auto-authenticated once that token was cleared. This surfaced in a bug bash:
gddy domain suggesthard-errored on the first run (token lackeddomains.domain:read), the suggestedauth login --scope …fix failed (it cleared the token, then requested a scope the OAuth client isn't registered for →no authorization code in callback), and re-runningdomain suggestthen auto-authed and succeeded. The error's advice was the wrong fix path; simply retrying was the answer.Fix
Drop the interactivity gate from the under-scoped branch so it takes the same
reauthenticate(union)path the no-token branch already uses. Missing scopes now always trigger step-up.plan_step_upsimplified toCovered/Reauthenticate(nointeractiveparam).StepUp::MissingNonInteractive,session_is_interactive, andmissing_scope_error(and theIsTerminalimport).defaults ∪ granted ∪ required, so step-up never narrows an existing grant; the post-authensure_grantedcheck is unchanged.Accepted tradeoff: a genuinely headless run holding an under-scoped token now opens a browser and times out after 120s (
timed out waiting for OAuth callback) instead of failing fast — identical to today's no-token behavior.Tests
plan_step_up_covers_or_reauthenticatesfor the two-outcome signature (missing →Reauthenticate, no interactivity axis).cargo fmt,cargo clippy --all-targets --all-features -D warnings, and fullcargo testall clean (134+ tests).🤖 Generated with Claude Code