Accumulate OAuth scopes across step-up authorization challenges#1473
Draft
Accumulate OAuth scopes across step-up authorization challenges#1473
Conversation
Agent-Logs-Url: https://github.com/modelcontextprotocol/csharp-sdk/sessions/cf7499bb-4f2e-430c-82c1-e160d5c12b25 Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Agent-Logs-Url: https://github.com/modelcontextprotocol/csharp-sdk/sessions/cf7499bb-4f2e-430c-82c1-e160d5c12b25 Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Fix scope parameter logic per SEP-2350
Accumulate OAuth scopes across step-up authorization challenges
Mar 29, 2026
Contributor
|
@copilot fix the build/test failures. Also re-evaluate the SEP and confirm these are all the changes and only the changes necessary to address it. |
…ngSplitOptions) Agent-Logs-Url: https://github.com/modelcontextprotocol/csharp-sdk/sessions/5e730596-6a29-45ba-8610-4bc03424893e Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Contributor
Author
Fixed the build failure in Re-evaluated against the spec (SEP-2350 diff): The spec requires clients to "compute the union of previously requested scopes and newly challenged scopes when re-authorizing." The changes are:
No other changes are necessary — no new public API, no other code paths affected. |
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.
Per the updated MCP spec (modelcontextprotocol#2350, aligned with RFC 6750 §3.1), servers should emit only per-operation scopes in
403 insufficient_scopechallenges. Clients SHOULD compute the union of previously requested scopes and newly challenged scopes to avoid silently dropping permissions from prior operations.Previously,
GetScopeParameter()returned only the challenged scope, losing any scopes from earlier authorization rounds.Changes
ClientOAuthProvider: Added_lastRequestedScopesfield;GetScopeParameter()now unions previous and new scopes via aHashSet<string>and persists the result for future accumulation:AuthorizationFlow_UsesScopeFromForbiddenHeadertest: Updated the post-step-up assertion from an exact-equality check on the challenge scopes to a containment check verifying the re-authorization request includes both the original scopes and the newly challenged scopes.Test middleware/tool assertion: Updated scope checks from
HasClaim("scope", exactValue)to split-and-containment logic, since the token now carries the accumulated scope string rather than an exact match.Original prompt
Context
The MCP spec PR modelcontextprotocol/modelcontextprotocol#2350 (merged 2026-03-28) clarifies that:
insufficient_scopechallenges (aligned with RFC 6750 §3.1) — they are no longer expected to include previously granted scopes in the challenge.Problem
The current
ClientOAuthProvider.GetScopeParameter()method returns only the scope from the current challenge (or metadata/configured scopes), without unioning it with previously requested scopes. This means that when a server follows the spec's "minimum approach" and returns only the per-operation scopes in a 403 challenge, the client will re-authorize with only those new scopes, potentially losing previously granted permissions.The relevant code is in
src/ModelContextProtocol.Core/Authentication/ClientOAuthProvider.cs:Required Changes
Add a field to
ClientOAuthProviderto track previously requested scopes (e.g.,private string? _lastRequestedScopes;).Update
GetScopeParameter()to compute the union of the previously requested scopes and the new scopes. When_lastRequestedScopesis not null and new scopes are determined, combine them using aHashSet<string>to deduplicate, then join back into a space-separated string. Store the result back into_lastRequestedScopesfor future accumulation.Update the existing test
AuthorizationFlow_UsesScopeFromForbiddenHeaderintests/ModelContextProtocol.AspNetCore.Tests/OAuth/AuthTests.cs. Currently, after initial auth with"mcp:tools"and a step-up 403 with"admin:read admin:write", the test assertsrequestedScopeequals"admin:read admin:write". With scope accumulation, the re-authorization should now request the union: the assertion should verify that the scope contains both the original scopes AND the new scopes (e.g.,"mcp:tools admin:read admin:write"— though order may vary since we're using a HashSet).Consider adding a new test that specifically validates scope accumulation behavior — e.g., initial auth gets scope
"read", then a 403 challenges with"write", and verify the re-auth request includes both"read write".Notes
GetScopeParametermethod is also called fromPerformDynamicClientRegistrationAsyncfor DCR — scope accumulation should apply there too since it flows through the same method..github/copilot-instructions.md).The following is the prior conversation context from the user's chat exploration (may be truncated):
User: Anything we need to do to react to this PR in the C# SDK?
Assistant:
Let me look at the PR to understand what changes were made to the MCP specification, and then check the current state of the C# SDK's authorization implementation.
Assistant: Now let me look at the actual diff of the spec PR and the current scope accumulation logic in the C# SDK more closely.
Assistant: Now let me look at what the spec PR actually changed to understand the specific requirements:
Assistant: Now I have a clear picture. Let me analyze the spec change and the current C# SDK implementation.
No immediate code changes are required, but there's a gap worth tracking. Here's the analysis:
What the spec PR changed
PR #2350 clarifies three things in the authorization spec:
insufficient_scopechallenges (aligned with RFC 6750 §3.1) — they're no longer expected to include previously granted scopes.This pull request was created from Copilot chat.
🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.