Conversation
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>
|
There was a problem hiding this comment.
🟡 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.
| 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. |
|
Closing. The default was deliberately left empty: RFC 6749 §3.3 allows it, it doesn't break clients that omit |



Stacked on #1958.
Problem
An authorization request that omits
scopepassed 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 resourcedeclares. 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
ResourceBindingLifecycleTestboth fail if the default is removed.scope:['datahub:read'], tokenscope: datahub:read, MCP 200.['mcp:read', 'mcp:write'], tokenscope: mcp:read mcp:write.🤖 Generated with Claude Code