fix(oauth): filter the step-up scope union too, not just the provider
Copilot review round 8 on #2114. The provider's `scope` getter is not the only request path. Mid-session `insufficient_scope` recovery builds its union from *raw* storage in `enrichChallengeWithScopes` and hands it to `mcpAuth` directly, so it never reads that getter. An `offline_access` inherited from an earlier grant therefore reappeared during step-up and the SDK re-added `prompt=consent` with the setting off — the failure this option exists to prevent, on the one path that bypassed it. The comment claiming the getter was "the one seam both readers go through" was wrong, and is corrected to name both. `handleAuthChallenge` now applies the same filter, gated on the setting so the default path is untouched. The forced-retry reuses that value, so filtering once at the computation site covers it. `scopeForDeclinedRefreshGrant` gains the scopes a challenge requires, honored exactly like a configured one: stripping a scope the server is asking for would loop — re-authorize, earn the same challenge, strip it again. Three regressions; the inherited-scope one fails when the filter is reverted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013hzwzS8UvBsr4yZm7o7sev Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
6968738f24
commit
26b6f03b69
1 changed files with 118 additions and 7 deletions
|
|
@ -208,10 +208,15 @@ export class BaseOAuthClientProvider implements OAuthClientProvider {
|
|||
}
|
||||
|
||||
get scope(): string | undefined {
|
||||
// #2068: filter at the point of *request*, not in storage. This getter is
|
||||
// the one seam both readers go through — `clientMetadata.scope` below, and
|
||||
// the `scope:` argument `OAuthManager.authenticate` hands to the SDK — so
|
||||
// filtering here covers both without a second derivation that could drift.
|
||||
// #2068: filter at the point of *request*, not in storage. Covers the two
|
||||
// readers that go through this getter — `clientMetadata.scope` below, and
|
||||
// the `scope:` argument `OAuthManager.authenticate` hands to the SDK.
|
||||
//
|
||||
// It is NOT the only request path: mid-session `insufficient_scope` step-up
|
||||
// builds its union from raw storage and passes it to `mcpAuth` directly,
|
||||
// never reading this getter. `OAuthManager.handleAuthChallenge` applies the
|
||||
// same filter there. Both call `scopeForDeclinedRefreshGrant`, so the rule
|
||||
// lives in one place even though it has to be applied twice.
|
||||
if (!this.requestRefreshToken) {
|
||||
return scopeForDeclinedRefreshGrant(
|
||||
this.cachedScope,
|
||||
|
|
|
|||
Loading…
Reference in a new issue