Skip to content

Commit 49f7dec

Browse files
ShivamShivam
authored andcommitted
fix: validate_scope treats None registered scopes as no restrictions
When a client was registered without specific scopes (scope=None), validate_scope() converted None to an empty list, causing all requested scopes to be rejected with InvalidScopeError. Now None is correctly treated as "no restrictions", allowing any scope. Closes #2216
1 parent b33c811 commit 49f7dec

File tree

2 files changed

+45
-4
lines changed

2 files changed

+45
-4
lines changed

src/mcp/shared/auth.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,14 @@ def validate_scope(self, requested_scope: str | None) -> list[str] | None:
7171
if requested_scope is None:
7272
return None
7373
requested_scopes = requested_scope.split(" ")
74-
allowed_scopes = [] if self.scope is None else self.scope.split(" ")
74+
if self.scope is None:
75+
# No registered scopes means no restrictions
76+
return requested_scopes
77+
allowed_scopes = self.scope.split(" ")
7578
for scope in requested_scopes:
76-
if scope not in allowed_scopes: # pragma: no branch
79+
if scope not in allowed_scopes:
7780
raise InvalidScopeError(f"Client was not registered with scope {scope}")
78-
return requested_scopes # pragma: no cover
81+
return requested_scopes
7982

8083
def validate_redirect_uri(self, redirect_uri: AnyUrl | None) -> AnyUrl:
8184
if redirect_uri is not None:

tests/shared/test_auth.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""Tests for OAuth 2.0 shared code."""
22

3-
from mcp.shared.auth import OAuthMetadata
3+
import pytest
4+
5+
from mcp.shared.auth import InvalidScopeError, OAuthClientMetadata, OAuthMetadata
46

57

68
def test_oauth():
@@ -58,3 +60,39 @@ def test_oauth_with_jarm():
5860
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
5961
}
6062
)
63+
64+
65+
class TestValidateScope:
66+
"""Tests for OAuthClientMetadata.validate_scope."""
67+
68+
def _make_client(self, scope: str | None = None) -> OAuthClientMetadata:
69+
return OAuthClientMetadata(redirect_uris=["https://example.com/callback"], scope=scope)
70+
71+
def test_none_requested_scope_returns_none(self):
72+
client = self._make_client(scope="read write")
73+
assert client.validate_scope(None) is None
74+
75+
def test_none_registered_scope_allows_any_requested_scope(self):
76+
client = self._make_client(scope=None)
77+
result = client.validate_scope("read write admin")
78+
assert result == ["read", "write", "admin"]
79+
80+
def test_registered_scope_allows_matching_requested_scope(self):
81+
client = self._make_client(scope="read write")
82+
result = client.validate_scope("read")
83+
assert result == ["read"]
84+
85+
def test_registered_scope_allows_all_matching_scopes(self):
86+
client = self._make_client(scope="read write")
87+
result = client.validate_scope("read write")
88+
assert result == ["read", "write"]
89+
90+
def test_registered_scope_rejects_unregistered_scope(self):
91+
client = self._make_client(scope="read write")
92+
with pytest.raises(InvalidScopeError, match="Client was not registered with scope admin"):
93+
client.validate_scope("read admin")
94+
95+
def test_empty_registered_scope_rejects_any_requested_scope(self):
96+
client = self._make_client(scope="")
97+
with pytest.raises(InvalidScopeError):
98+
client.validate_scope("read")

0 commit comments

Comments
 (0)