diff --git a/tests/server/auth/oauth_proxy/conftest.py b/tests/server/auth/oauth_proxy/conftest.py index 3acfacf7f..802ca2348 100644 --- a/tests/server/auth/oauth_proxy/conftest.py +++ b/tests/server/auth/oauth_proxy/conftest.py @@ -288,6 +288,8 @@ def jwt_verifier(): @pytest.fixture def oauth_proxy(jwt_verifier): """Create a standard OAuthProxy instance for testing.""" + from key_value.aio.stores.memory import MemoryStore + return OAuthProxy( upstream_authorization_endpoint="https://github.com/login/oauth/authorize", upstream_token_endpoint="https://github.com/login/oauth/access_token", @@ -297,6 +299,7 @@ def oauth_proxy(jwt_verifier): base_url="https://myserver.com", redirect_path="/auth/callback", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) diff --git a/tests/server/auth/oauth_proxy/test_authorization.py b/tests/server/auth/oauth_proxy/test_authorization.py index 2b5aaf4a2..7a8a9b9e7 100644 --- a/tests/server/auth/oauth_proxy/test_authorization.py +++ b/tests/server/auth/oauth_proxy/test_authorization.py @@ -3,6 +3,7 @@ from urllib.parse import parse_qs, urlparse import pytest +from key_value.aio.stores.memory import MemoryStore from mcp.server.auth.provider import AuthorizationParams from mcp.shared.auth import OAuthClientInformationFull from pydantic import AnyUrl @@ -67,6 +68,7 @@ class TestOAuthProxyPKCE: base_url="https://proxy.example.com", forward_pkce=True, jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) @pytest.fixture @@ -82,6 +84,7 @@ class TestOAuthProxyPKCE: base_url="https://proxy.example.com", forward_pkce=False, jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) async def test_pkce_forwarding_enabled(self, proxy_with_pkce): @@ -172,6 +175,7 @@ class TestParameterForwarding: "prompt": "consent", "max_age": "3600", }, + client_storage=MemoryStore(), ) client = OAuthClientInformationFull( diff --git a/tests/server/auth/oauth_proxy/test_config.py b/tests/server/auth/oauth_proxy/test_config.py index ae3abe002..0b88a0ae5 100644 --- a/tests/server/auth/oauth_proxy/test_config.py +++ b/tests/server/auth/oauth_proxy/test_config.py @@ -1,6 +1,7 @@ """Tests for OAuth proxy configuration and validation.""" import pytest +from key_value.aio.stores.memory import MemoryStore from mcp.server.auth.provider import AuthorizationParams, AuthorizeError from mcp.shared.auth import OAuthClientInformationFull from pydantic import AnyHttpUrl, AnyUrl @@ -76,6 +77,7 @@ class TestResourceURLValidation: token_verifier=jwt_verifier, base_url="https://proxy.example.com", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) # Use non-default path to prove fix isn't relying on old hardcoded /mcp proxy.set_mcp_path("/api/v2/mcp") @@ -261,6 +263,7 @@ class TestResourceURLValidation: token_verifier=jwt_verifier, base_url="https://proxy.example.com", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) proxy.set_mcp_path("/mcp") # Simulate server configured with query params for tenant scoping @@ -300,6 +303,7 @@ class TestResourceURLValidation: token_verifier=jwt_verifier, base_url="https://proxy.example.com", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) proxy.set_mcp_path("/mcp") # Simulate server configured with query params for tenant scoping @@ -337,6 +341,7 @@ class TestResourceURLValidation: token_verifier=jwt_verifier, base_url="https://proxy.example.com", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) proxy.set_mcp_path("/mcp") # Simulate server configured with query params for tenant scoping @@ -374,6 +379,7 @@ class TestResourceURLValidation: token_verifier=jwt_verifier, base_url="https://proxy.example.com", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) # Before set_mcp_path, _jwt_issuer is None @@ -397,6 +403,7 @@ class TestResourceURLValidation: token_verifier=jwt_verifier, base_url="https://proxy.example.com", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) proxy.set_mcp_path(None) @@ -413,6 +420,7 @@ class TestResourceURLValidation: token_verifier=jwt_verifier, base_url="https://proxy.example.com", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) with pytest.raises(RuntimeError) as exc_info: @@ -430,6 +438,7 @@ class TestResourceURLValidation: token_verifier=jwt_verifier, base_url="https://proxy.example.com", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) # Before get_routes, _jwt_issuer is None diff --git a/tests/server/auth/oauth_proxy/test_e2e.py b/tests/server/auth/oauth_proxy/test_e2e.py index 8b500db61..39c4592e8 100644 --- a/tests/server/auth/oauth_proxy/test_e2e.py +++ b/tests/server/auth/oauth_proxy/test_e2e.py @@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, patch from urllib.parse import parse_qs, urlparse import httpx +from key_value.aio.stores.memory import MemoryStore from mcp.server.auth.provider import AuthorizationCode, AuthorizationParams from mcp.shared.auth import OAuthClientInformationFull from pydantic import AnyUrl @@ -30,6 +31,7 @@ class TestOAuthProxyE2E: token_verifier=MockTokenVerifier(), base_url="http://localhost:8000", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) # Create FastMCP server with proxy @@ -84,6 +86,7 @@ class TestOAuthProxyE2E: token_verifier=MockTokenVerifier(), base_url="http://localhost:8000", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) # Initialize JWT issuer before token operations @@ -201,6 +204,7 @@ class TestOAuthProxyE2E: base_url="http://localhost:8000", forward_pkce=True, # Enable PKCE forwarding jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) client = OAuthClientInformationFull( diff --git a/tests/server/auth/oauth_proxy/test_oauth_proxy.py b/tests/server/auth/oauth_proxy/test_oauth_proxy.py index a8cfac54d..27c31e198 100644 --- a/tests/server/auth/oauth_proxy/test_oauth_proxy.py +++ b/tests/server/auth/oauth_proxy/test_oauth_proxy.py @@ -1,5 +1,7 @@ """Tests for OAuth proxy initialization and configuration.""" +from key_value.aio.stores.memory import MemoryStore + from fastmcp.server.auth.oauth_proxy import OAuthProxy @@ -16,6 +18,7 @@ class TestOAuthProxyInitialization: token_verifier=jwt_verifier, base_url="https://api.example.com", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) assert ( @@ -45,6 +48,7 @@ class TestOAuthProxyInitialization: forward_pkce=False, token_endpoint_auth_method="client_secret_post", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) assert proxy._upstream_revocation_endpoint == "https://auth.example.com/revoke" @@ -65,5 +69,6 @@ class TestOAuthProxyInitialization: base_url="https://api.com", redirect_path="auth/callback", # No leading slash jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) assert proxy._redirect_path == "/auth/callback" diff --git a/tests/server/auth/oauth_proxy/test_tokens.py b/tests/server/auth/oauth_proxy/test_tokens.py index 5051a8ebc..b7e16431f 100644 --- a/tests/server/auth/oauth_proxy/test_tokens.py +++ b/tests/server/auth/oauth_proxy/test_tokens.py @@ -4,6 +4,7 @@ import time from unittest.mock import AsyncMock, Mock, patch import pytest +from key_value.aio.stores.memory import MemoryStore from mcp.server.auth.handlers.token import TokenErrorResponse from mcp.server.auth.handlers.token import TokenHandler as SDKTokenHandler from mcp.server.auth.provider import AuthorizationCode @@ -35,6 +36,7 @@ class TestOAuthProxyTokenEndpointAuth: base_url="https://proxy.example.com", token_endpoint_auth_method="client_secret_post", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) assert proxy_post._token_endpoint_auth_method == "client_secret_post" @@ -48,6 +50,7 @@ class TestOAuthProxyTokenEndpointAuth: base_url="https://proxy.example.com", token_endpoint_auth_method="client_secret_basic", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) assert proxy_basic._token_endpoint_auth_method == "client_secret_basic" @@ -60,6 +63,7 @@ class TestOAuthProxyTokenEndpointAuth: token_verifier=jwt_verifier, base_url="https://proxy.example.com", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) assert proxy_default._token_endpoint_auth_method is None @@ -74,6 +78,7 @@ class TestOAuthProxyTokenEndpointAuth: base_url="https://proxy.example.com", token_endpoint_auth_method="client_secret_post", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) # Initialize JWT issuer before token operations @@ -296,6 +301,7 @@ class TestFallbackAccessTokenExpiry: base_url="http://localhost:8000", jwt_signing_key="test-signing-key", fallback_access_token_expiry_seconds=86400, + client_storage=MemoryStore(), ) assert provider._fallback_access_token_expiry_seconds == 86400 @@ -313,6 +319,7 @@ class TestFallbackAccessTokenExpiry: ), base_url="http://localhost:8000", jwt_signing_key="test-signing-key", + client_storage=MemoryStore(), ) assert provider._fallback_access_token_expiry_seconds is None @@ -345,6 +352,7 @@ class TestUpstreamTokenStorageTTL: token_verifier=jwt_verifier, base_url="https://proxy.example.com", jwt_signing_key="test-secret-key", + client_storage=MemoryStore(), ) proxy.set_mcp_path("/mcp") return proxy diff --git a/tests/server/auth/oauth_proxy/test_ui.py b/tests/server/auth/oauth_proxy/test_ui.py index 9967d5a31..4795ec2ff 100644 --- a/tests/server/auth/oauth_proxy/test_ui.py +++ b/tests/server/auth/oauth_proxy/test_ui.py @@ -2,6 +2,7 @@ from unittest.mock import Mock +from key_value.aio.stores.memory import MemoryStore from starlette.requests import Request from starlette.responses import HTMLResponse @@ -76,6 +77,7 @@ class TestErrorPageRendering: ), base_url="http://localhost:8000", jwt_signing_key="test-signing-key", + client_storage=MemoryStore(), ) # Mock a request with an error from the IdP diff --git a/tests/server/auth/test_enhanced_error_responses.py b/tests/server/auth/test_enhanced_error_responses.py index cec425ecc..f7463be36 100644 --- a/tests/server/auth/test_enhanced_error_responses.py +++ b/tests/server/auth/test_enhanced_error_responses.py @@ -29,6 +29,8 @@ class TestEnhancedAuthorizationHandler: @pytest.fixture def oauth_proxy(self, rsa_key_pair): """Create OAuth proxy for testing.""" + from key_value.aio.stores.memory import MemoryStore + return OAuthProxy( upstream_authorization_endpoint="https://github.com/login/oauth/authorize", upstream_token_endpoint="https://github.com/login/oauth/access_token", @@ -42,6 +44,7 @@ class TestEnhancedAuthorizationHandler: ), base_url="https://myserver.com", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) def test_unregistered_client_returns_html_for_browser(self, oauth_proxy): @@ -290,6 +293,8 @@ class TestContentNegotiation: @pytest.fixture def oauth_proxy(self): """Create OAuth proxy for testing.""" + from key_value.aio.stores.memory import MemoryStore + return OAuthProxy( upstream_authorization_endpoint="https://github.com/login/oauth/authorize", upstream_token_endpoint="https://github.com/login/oauth/access_token", @@ -303,6 +308,7 @@ class TestContentNegotiation: ), base_url="https://myserver.com", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) def test_html_preferred_when_both_accepted(self, oauth_proxy): diff --git a/tests/server/auth/test_oauth_mounting.py b/tests/server/auth/test_oauth_mounting.py index 1729c5979..93f4a2308 100644 --- a/tests/server/auth/test_oauth_mounting.py +++ b/tests/server/auth/test_oauth_mounting.py @@ -8,6 +8,7 @@ The fix uses MCP SDK 1.17+ which implements RFC 9728 path-scoped well-known URLs import httpx import pytest +from key_value.aio.stores.memory import MemoryStore from pydantic import AnyHttpUrl from starlette.applications import Starlette from starlette.routing import Mount @@ -220,6 +221,7 @@ class TestOAuthMounting: token_verifier=token_verifier, base_url="https://api.example.com/api", # Includes mount prefix issuer_url="https://api.example.com", # Root level + client_storage=MemoryStore(), ) mcp = FastMCP("test-server", auth=auth_provider) @@ -290,6 +292,7 @@ class TestOAuthMounting: upstream_client_secret="test-client-secret", token_verifier=token_verifier, base_url="https://api.example.com/api", # Has path, no explicit issuer_url + client_storage=MemoryStore(), ) mcp = FastMCP("test-server", auth=auth_provider) @@ -366,6 +369,7 @@ class TestOAuthMounting: token_verifier=token_verifier, base_url="https://api.example.com/api", issuer_url="https://api.example.com", # Explicitly root + client_storage=MemoryStore(), ) well_known_routes = auth_provider.get_well_known_routes(mcp_path="/mcp") diff --git a/tests/server/auth/test_oauth_proxy_redirect_validation.py b/tests/server/auth/test_oauth_proxy_redirect_validation.py index c91560c52..336029197 100644 --- a/tests/server/auth/test_oauth_proxy_redirect_validation.py +++ b/tests/server/auth/test_oauth_proxy_redirect_validation.py @@ -1,6 +1,7 @@ """Tests for OAuth proxy redirect URI validation.""" import pytest +from key_value.aio.stores.memory import MemoryStore from mcp.shared.auth import InvalidRedirectUriError from pydantic import AnyUrl @@ -112,6 +113,7 @@ class TestOAuthProxyRedirectValidation: token_verifier=MockTokenVerifier(), base_url="http://localhost:8000", jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) # The proxy should store None for default (allow all) @@ -130,6 +132,7 @@ class TestOAuthProxyRedirectValidation: base_url="http://localhost:8000", allowed_client_redirect_uris=custom_patterns, jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) assert proxy._allowed_client_redirect_uris == custom_patterns @@ -145,6 +148,7 @@ class TestOAuthProxyRedirectValidation: base_url="http://localhost:8000", allowed_client_redirect_uris=[], jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) assert proxy._allowed_client_redirect_uris == [] @@ -162,6 +166,7 @@ class TestOAuthProxyRedirectValidation: base_url="http://localhost:8000", allowed_client_redirect_uris=custom_patterns, jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) # Register a client @@ -195,6 +200,7 @@ class TestOAuthProxyRedirectValidation: base_url="http://localhost:8000", allowed_client_redirect_uris=custom_patterns, jwt_signing_key="test-secret", + client_storage=MemoryStore(), ) # Get an unregistered client