-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Expand file tree
/
Copy pathtest_auth.py
More file actions
112 lines (94 loc) · 4.44 KB
/
test_auth.py
File metadata and controls
112 lines (94 loc) · 4.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
"""Tests for OAuth 2.0 shared code."""
import pytest
from pydantic import AnyUrl
from mcp.shared.auth import InvalidScopeError, OAuthClientMetadata, OAuthMetadata
def test_oauth():
"""Should not throw when parsing OAuth metadata."""
OAuthMetadata.model_validate(
{
"issuer": "https://example.com",
"authorization_endpoint": "https://example.com/oauth2/authorize",
"token_endpoint": "https://example.com/oauth2/token",
"scopes_supported": ["read", "write"],
"response_types_supported": ["code", "token"],
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
}
)
def test_oidc():
"""Should not throw when parsing OIDC metadata."""
OAuthMetadata.model_validate(
{
"issuer": "https://example.com",
"authorization_endpoint": "https://example.com/oauth2/authorize",
"token_endpoint": "https://example.com/oauth2/token",
"end_session_endpoint": "https://example.com/logout",
"id_token_signing_alg_values_supported": ["RS256"],
"jwks_uri": "https://example.com/.well-known/jwks.json",
"response_types_supported": ["code", "token"],
"revocation_endpoint": "https://example.com/oauth2/revoke",
"scopes_supported": ["openid", "read", "write"],
"subject_types_supported": ["public"],
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
"userinfo_endpoint": "https://example.com/oauth2/userInfo",
}
)
def test_oauth_with_jarm():
"""Should not throw when parsing OAuth metadata that includes JARM response modes."""
OAuthMetadata.model_validate(
{
"issuer": "https://example.com",
"authorization_endpoint": "https://example.com/oauth2/authorize",
"token_endpoint": "https://example.com/oauth2/token",
"scopes_supported": ["read", "write"],
"response_types_supported": ["code", "token"],
"response_modes_supported": [
"query",
"fragment",
"form_post",
"query.jwt",
"fragment.jwt",
"form_post.jwt",
"jwt",
],
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
}
)
class TestValidateScope:
"""Tests for OAuthClientMetadata.validate_scope()."""
def _make_client(self, scope=None):
return OAuthClientMetadata(
redirect_uris=[AnyUrl("http://localhost:3000/callback")],
scope=scope,
)
def test_requested_none_returns_none(self):
"""When no scope is requested, validate_scope returns None."""
client = self._make_client(scope="read write")
assert client.validate_scope(None) is None
def test_client_scope_none_allows_any_requested_scopes(self):
"""When client has no scope restrictions (None), any requested scopes are allowed.
Regression test for #2216: validate_scope treated None as empty list,
rejecting all scopes with InvalidScopeError.
"""
client = self._make_client(scope=None)
result = client.validate_scope("read write admin")
assert result == ["read", "write", "admin"]
def test_client_scope_none_allows_single_scope(self):
"""When client has no scope restrictions, a single requested scope is allowed."""
client = self._make_client(scope=None)
result = client.validate_scope("read")
assert result == ["read"]
def test_allowed_scopes_accepted(self):
"""Requested scopes that are a subset of client scopes are accepted."""
client = self._make_client(scope="read write admin")
result = client.validate_scope("read write")
assert result == ["read", "write"]
def test_disallowed_scope_raises(self):
"""Requesting a scope not in the client's registered scopes raises InvalidScopeError."""
client = self._make_client(scope="read write")
with pytest.raises(InvalidScopeError, match="admin"):
client.validate_scope("read admin")
def test_all_scopes_match(self):
"""Requesting exactly the registered scopes works."""
client = self._make_client(scope="read write")
result = client.validate_scope("read write")
assert result == ["read", "write"]