Skip to content

[OAuth] Give a request without scope the resource's declared scopes - #2047

Closed
fashxp wants to merge 1 commit into
feature/oauth-1308-basic-integrationfrom
feature/oauth-default-resource-scopes
Closed

fashxp wants to merge 1 commit into
feature/oauth-1308-basic-integrationfrom
feature/oauth-default-resource-scopes

Conversation

@fashxp

@fashxp fashxp commented Sep 16, 2026

Copy link
Copy Markdown
Member

Stacked on #1958.

Problem

An authorization request that omits scope passed through as an empty set. The consent screen then said
"No specific permissions were requested", and the resulting token still reached the resource, because nothing
checks scopes at call time. Found through Datahub Simple REST (pimcore/data-hub-simple-rest#356), which now
refuses such a token itself; Studio's own MCP servers had the same gap.

Change

RFC 6749 section 3.3 lets the server either apply a default or refuse. Refusing would break clients that work
today, so LoopbackAuthCodeGrant::narrowToResource() now defaults an empty request to every scope the resource
declares. The consent screen shows those scopes and the token carries them. A resource declaring no scopes is
unchanged.

The docs for protected applications now tell them to check their scope even when every operation has the same
privilege. The reason is refresh: a client may ask for fewer scopes than were granted, including none, and no
consent screen is shown then.

Verified

  • Unit: 1039 tests pass, PHPStan clean. The new grant test and the new end-to-end case in
    ResourceBindingLifecycleTest both fail if the default is removed.
  • Live, request without scope:
    • Datahub MCP: consent ['datahub:read'], token scope: datahub:read, MCP 200.
    • Studio MCP: consent ['mcp:read', 'mcp:write'], token scope: mcp:read mcp:write.

🤖 Generated with Claude Code

An omitted scope passed through as an empty set, so the consent screen said nothing
was requested while the token still reached the resource.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings September 16, 2026 11:58
@sonarqubecloud

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The refresh-token documentation inaccurately states that clients can refresh with no scopes.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Defaults scope-less authorization requests to all scopes declared by the target OAuth resource.

Changes:

  • Applies resource-declared scopes when authorization omits scope.
  • Adds unit and end-to-end regression coverage.
  • Updates OAuth documentation and security guidance.

Review assessment:

  • Root cause: Fixed at the grant’s authorization boundary (LoopbackAuthCodeGrant.php:125-142).
  • Call sites: The shared authorization path applies narrowing consistently (LoopbackAuthCodeGrant.php:63-76).
  • Compatibility: No public signature changes; token scope behavior intentionally changes.
  • Tests: Unit and lifecycle tests cover scoped and scope-less resources.
  • Remaining issue: Refresh tokens cannot request “none” as documented (07_OAuth_Protected_Applications.md:380-382); omitted or empty scope preserves the original scopes.
File summaries
File Description
src/OAuth/Server/Grant/LoopbackAuthCodeGrant.php Defaults omitted scopes from the resource declaration.
tests/Unit/OAuth/Server/Grant/LoopbackAuthCodeGrantTest.php Tests defaults and resources without scopes.
tests/Unit/OAuth/Server/ResourceBindingLifecycleTest.php Verifies default scopes reach issued tokens.
doc/04_Development_Details/07_OAuth_Protected_Applications.md Documents resource enforcement guidance.
doc/02_Installation_and_Configuration/06_OAuth_Server.md Documents authorization scope defaults.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Balanced (auto)

Note

Copilot is running an experiment and ran this review at Balanced.


💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +380 to +382
Check for your scope even when every operation has the same privilege. The authorization request always leads to
a consent screen listing your scopes, but a client refreshing a token may ask for fewer scopes than were granted,
including none, and no screen is shown then. Requiring your scope keeps such a token from reaching your resource.
@fashxp

fashxp commented Sep 16, 2026

Copy link
Copy Markdown
Member Author

Closing. The default was deliberately left empty: RFC 6749 §3.3 allows it, it doesn't break clients that omit scope, and it's the least-privilege choice once resources enforce their scopes (Datahub does now, Studio MCP will with per-tool scopes). Defaulting to every declared scope would hand such a client maximal privilege. The misleading consent wording for an empty scope list is handled in the UI instead, and the "check your scope" guidance for protected applications moves into #1958.

@fashxp fashxp closed this Sep 16, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 16, 2026
@fashxp
fashxp deleted the feature/oauth-default-resource-scopes branch September 17, 2026 07:08
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants