|
10 | 10 |
|
11 | 11 | import httpx |
12 | 12 | import pytest |
| 13 | +from inline_snapshot import snapshot |
13 | 14 | from pydantic import AnyHttpUrl |
14 | 15 |
|
15 | 16 | from mcp.client.auth import OAuthClientProvider |
| 17 | +from mcp.server.auth.routes import build_metadata |
| 18 | +from mcp.server.auth.settings import ClientRegistrationOptions, RevocationOptions |
16 | 19 | from mcp.shared.auth import ( |
17 | 20 | OAuthClientInformationFull, |
18 | 21 | OAuthClientMetadata, |
@@ -356,7 +359,8 @@ def test_has_valid_token_valid(self, oauth_provider, oauth_token): |
356 | 359 |
|
357 | 360 | assert oauth_provider._has_valid_token() |
358 | 361 |
|
359 | | - def test_has_valid_token_expired(self, oauth_provider, oauth_token): |
| 362 | + @pytest.mark.anyio |
| 363 | + async def test_has_valid_token_expired(self, oauth_provider, oauth_token): |
360 | 364 | """Test token validation with expired token.""" |
361 | 365 | oauth_provider._current_tokens = oauth_token |
362 | 366 | oauth_provider._token_expiry_time = time.time() - 3600 # Past expiry |
@@ -807,7 +811,8 @@ def test_scope_priority_no_client_metadata_scope( |
807 | 811 | # No scope should be set since client metadata doesn't have explicit scope |
808 | 812 | assert "scope" not in auth_params |
809 | 813 |
|
810 | | - def test_scope_priority_no_scope(self, oauth_provider, oauth_client_info): |
| 814 | + @pytest.mark.anyio |
| 815 | + async def test_scope_priority_no_scope(self, oauth_provider, oauth_client_info): |
811 | 816 | """Test that no scope parameter is set when no scopes specified.""" |
812 | 817 | oauth_provider.client_metadata.scope = None |
813 | 818 | oauth_provider._client_info = oauth_client_info |
@@ -905,3 +910,76 @@ async def test_token_exchange_error_basic(self, oauth_provider, oauth_client_inf |
905 | 910 | await oauth_provider._exchange_code_for_token( |
906 | 911 | "invalid_auth_code", oauth_client_info |
907 | 912 | ) |
| 913 | + |
| 914 | + |
| 915 | +@pytest.mark.parametrize( |
| 916 | + ( |
| 917 | + "issuer_url", |
| 918 | + "service_documentation_url", |
| 919 | + "authorization_endpoint", |
| 920 | + "token_endpoint", |
| 921 | + "registration_endpoint", |
| 922 | + "revocation_endpoint", |
| 923 | + ), |
| 924 | + ( |
| 925 | + pytest.param( |
| 926 | + "https://auth.example.com", |
| 927 | + "https://auth.example.com/docs", |
| 928 | + "https://auth.example.com/authorize", |
| 929 | + "https://auth.example.com/token", |
| 930 | + "https://auth.example.com/register", |
| 931 | + "https://auth.example.com/revoke", |
| 932 | + id="simple-url", |
| 933 | + ), |
| 934 | + pytest.param( |
| 935 | + "https://auth.example.com/", |
| 936 | + "https://auth.example.com/docs", |
| 937 | + "https://auth.example.com/authorize", |
| 938 | + "https://auth.example.com/token", |
| 939 | + "https://auth.example.com/register", |
| 940 | + "https://auth.example.com/revoke", |
| 941 | + id="with-trailing-slash", |
| 942 | + ), |
| 943 | + pytest.param( |
| 944 | + "https://auth.example.com/v1/mcp", |
| 945 | + "https://auth.example.com/v1/mcp/docs", |
| 946 | + "https://auth.example.com/v1/mcp/authorize", |
| 947 | + "https://auth.example.com/v1/mcp/token", |
| 948 | + "https://auth.example.com/v1/mcp/register", |
| 949 | + "https://auth.example.com/v1/mcp/revoke", |
| 950 | + id="with-path-param", |
| 951 | + ), |
| 952 | + ), |
| 953 | +) |
| 954 | +def test_build_metadata( |
| 955 | + issuer_url: str, |
| 956 | + service_documentation_url: str, |
| 957 | + authorization_endpoint: str, |
| 958 | + token_endpoint: str, |
| 959 | + registration_endpoint: str, |
| 960 | + revocation_endpoint: str, |
| 961 | +): |
| 962 | + metadata = build_metadata( |
| 963 | + issuer_url=AnyHttpUrl(issuer_url), |
| 964 | + service_documentation_url=AnyHttpUrl(service_documentation_url), |
| 965 | + client_registration_options=ClientRegistrationOptions( |
| 966 | + enabled=True, valid_scopes=["read", "write", "admin"] |
| 967 | + ), |
| 968 | + revocation_options=RevocationOptions(enabled=True), |
| 969 | + ) |
| 970 | + |
| 971 | + assert metadata == snapshot( |
| 972 | + OAuthMetadata( |
| 973 | + issuer=AnyHttpUrl(issuer_url), |
| 974 | + authorization_endpoint=AnyHttpUrl(authorization_endpoint), |
| 975 | + token_endpoint=AnyHttpUrl(token_endpoint), |
| 976 | + registration_endpoint=AnyHttpUrl(registration_endpoint), |
| 977 | + scopes_supported=["read", "write", "admin"], |
| 978 | + grant_types_supported=["authorization_code", "refresh_token"], |
| 979 | + token_endpoint_auth_methods_supported=["client_secret_post"], |
| 980 | + service_documentation=AnyHttpUrl(service_documentation_url), |
| 981 | + revocation_endpoint=AnyHttpUrl(revocation_endpoint), |
| 982 | + revocation_endpoint_auth_methods_supported=["client_secret_post"], |
| 983 | + code_challenge_methods_supported=["S256"], |
| 984 | + ) |
| 985 | + ) |
0 commit comments