Skip to content

Add a scope selector for OAuth authorization-code clients - #579

Open
atesgoral wants to merge 1 commit into
modelcontextprotocol:mainfrom
atesgoral:ag/oauth-explicit-empty-scope
Open

atesgoral wants to merge 1 commit into
modelcontextprotocol:mainfrom
atesgoral:ag/oauth-explicit-empty-scope

Conversation

@atesgoral

@atesgoral atesgoral commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Why

The MCP 2026-07-28 scope-selection strategy recommends the challenge's scope, then PRM scopes_supported (intended as a minimal basic set). The Ruby SDK follows that default. Some hosted clients need a narrower per-user policy, including a first authorization request with no client-provided scope parameter; authorization_request_validator can refuse but cannot change the selected scopes.

Fixes #578.

What changes

Add optional Provider.new(scope_selector:) for authorization-code clients. It receives a read-only array after the SDK's normal selection and offline_access augmentation, and returns valid replacement scope tokens or nil / [] to omit the URL's scope parameter. When configured, the selector also overrides prefilled scope parameters in the authorization endpoint query, so validation and the browser URL agree; no-selector behavior is preserved. It runs before authorization-request validation and client registration. Unsupported offline_access remains stripped. Filtering challenged scopes may leave the operation unauthorized; omitting scope does not prevent the AS from applying defaults (RFC 6749 §3.3).

With no selector, the SDK's spec-aligned default and other OAuth grants are unchanged. This follows the application-policy hook pattern in the C# SDK and Go SDK.

Scope selection, illustrated

These examples assume no challenged scope, a Protected Resource Metadata (PRM) document advertising read write admin, and an authorization server (AS) supporting offline_access with a client declaring the refresh_token grant. The authorization-request validator shown here is optional.

Default versus application policy

OAuth scope selection branches: unchanged default, narrower selection, or no client scope parameter

The hook changes what the client requests, not what the AS grants. Returning nil or [] omits the client’s scope parameter; it does not prevent AS defaults.

Placement in a hosted OAuth flow

Hosted OAuth sequence showing scope selection before validation, registration, and browser redirect

This uses the existing two-leg OAuth flow from PR 573. The scope selector is the addition here; callback persistence and Flow#finish! are existing behavior. The host must inspect the actual granted scopes. Removing prefilled endpoint scopes when a selector is configured keeps the browser request consistent with the validator’s decision.

Validation

  • All 11 OAuth test files pass locally: 424 tests, 1,366 assertions.
  • Ruby syntax checks and git diff --check pass.
  • Bundled RuboCop cannot bootstrap locally because this checkout lacks puma/native extensions; upstream CI validates lint.

@atesgoral atesgoral changed the title Let OAuth clients select only explicit scopes Add a scope selector for OAuth authorization-code clients Sep 28, 2026
@atesgoral
atesgoral force-pushed the ag/oauth-explicit-empty-scope branch from 11d7b49 to 7194f1e Compare October 1, 2026 19:39
@atesgoral
atesgoral marked this pull request as ready for review October 1, 2026 19:45
@atesgoral
atesgoral requested a review from koic October 1, 2026 19:48

This branch has not been deployed

No deployments
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.

Let OAuth clients filter requested scopes

1 participant