99if TYPE_CHECKING :
1010 from fastmcp .server .auth .auth import AuthProvider
1111 from fastmcp .server .auth import JWTVerifier , StaticTokenVerifier
12- from mcp .server .auth .settings import ClientRegistrationOptions , RevocationOptions
12+ from mcp .server .auth .settings import RevocationOptions
1313
1414from .providers import (
1515 AuthConfig ,
1818 OAuthServerConfig ,
1919 RemoteAuthConfig ,
2020)
21+ from .registry import (
22+ get_provider_registry ,
23+ create_auth_provider_from_registry ,
24+ )
2125
2226
2327def create_auth_provider (config : AuthConfig ) -> "AuthProvider" :
2428 """Create a FastMCP AuthProvider from Golf auth configuration.
2529
30+ This function uses the provider registry system to allow extensibility.
31+ Built-in providers are automatically registered, and custom providers
32+ can be added via the registry system.
33+
2634 Args:
2735 config: Golf authentication configuration
2836
@@ -32,17 +40,23 @@ def create_auth_provider(config: AuthConfig) -> "AuthProvider":
3240 Raises:
3341 ValueError: If configuration is invalid
3442 ImportError: If required dependencies are missing
43+ KeyError: If provider type is not registered
3544 """
36- if config .provider_type == "jwt" :
37- return _create_jwt_provider (config )
38- elif config .provider_type == "static" :
39- return _create_static_provider (config )
40- elif config .provider_type == "oauth_server" :
41- return _create_oauth_server_provider (config )
42- elif config .provider_type == "remote" :
43- return _create_remote_provider (config )
44- else :
45- raise ValueError (f"Unknown provider type: { config .provider_type } " )
45+ try :
46+ return create_auth_provider_from_registry (config )
47+ except KeyError :
48+ # Fall back to legacy dispatch for backward compatibility
49+ # This ensures existing code continues to work during transition
50+ if config .provider_type == "jwt" :
51+ return _create_jwt_provider (config )
52+ elif config .provider_type == "static" :
53+ return _create_static_provider (config )
54+ elif config .provider_type == "oauth_server" :
55+ return _create_oauth_server_provider (config )
56+ elif config .provider_type == "remote" :
57+ return _create_remote_provider (config )
58+ else :
59+ raise ValueError (f"Unknown provider type: { config .provider_type } " ) from None
4660
4761
4862def _create_jwt_provider (config : JWTAuthConfig ) -> "JWTVerifier" :
@@ -123,21 +137,61 @@ def _create_oauth_server_provider(config: OAuthServerConfig) -> "AuthProvider":
123137 "OAuthProvider not available in this FastMCP version. Please upgrade to FastMCP 2.11.0 or later."
124138 ) from e
125139
126- # Resolve runtime values from environment variables
140+ # Resolve runtime values from environment variables with validation
127141 base_url = config .base_url
128142 if config .base_url_env_var :
129143 env_value = os .environ .get (config .base_url_env_var )
130144 if env_value :
131- base_url = env_value
145+ # Apply the same validation as the config field to env var value
146+ try :
147+ from urllib .parse import urlparse
148+
149+ env_value = env_value .strip ()
150+ parsed = urlparse (env_value )
151+
152+ if not parsed .scheme or not parsed .netloc :
153+ raise ValueError (
154+ f"Invalid base URL from environment variable { config .base_url_env_var } : '{ env_value } '"
155+ )
156+
157+ if parsed .scheme not in ("http" , "https" ):
158+ raise ValueError (f"Base URL from environment must use http/https: '{ env_value } '" )
159+
160+ # Production HTTPS check
161+ is_production = (
162+ os .environ .get ("GOLF_ENV" , "" ).lower () in ("prod" , "production" )
163+ or os .environ .get ("NODE_ENV" , "" ).lower () == "production"
164+ or os .environ .get ("ENVIRONMENT" , "" ).lower () in ("prod" , "production" )
165+ )
166+
167+ if is_production and parsed .scheme == "http" :
168+ raise ValueError (f"Base URL must use HTTPS in production: '{ env_value } '" )
169+
170+ base_url = env_value
171+
172+ except Exception as e :
173+ raise ValueError (f"Invalid base URL from environment variable { config .base_url_env_var } : { e } " ) from e
174+
175+ # Additional security validations before creating provider
176+ from urllib .parse import urlparse
177+
178+ # Validate final base_url
179+ parsed_base = urlparse (base_url )
180+ if not parsed_base .scheme or not parsed_base .netloc :
181+ raise ValueError (f"Invalid base URL: '{ base_url } '" )
182+
183+ # Security check: prevent localhost in production
184+ is_production = (
185+ os .environ .get ("GOLF_ENV" , "" ).lower () in ("prod" , "production" )
186+ or os .environ .get ("NODE_ENV" , "" ).lower () == "production"
187+ or os .environ .get ("ENVIRONMENT" , "" ).lower () in ("prod" , "production" )
188+ )
189+
190+ if is_production and parsed_base .hostname in ("localhost" , "127.0.0.1" , "0.0.0.0" ):
191+ raise ValueError (f"Cannot use localhost/loopback addresses in production: '{ base_url } '" )
132192
133- # Create client registration options
193+ # Client registration options - always disabled for security
134194 client_reg_options = None
135- if config .allow_client_registration :
136- client_reg_options = ClientRegistrationOptions (
137- enabled = True ,
138- valid_scopes = config .valid_scopes ,
139- default_scopes = config .default_scopes ,
140- )
141195
142196 # Create revocation options
143197 revocation_options = None
@@ -163,6 +217,20 @@ def _create_remote_provider(config: RemoteAuthConfig) -> "AuthProvider":
163217 "RemoteAuthProvider not available in this FastMCP version. Please upgrade to FastMCP 2.11.0 or later."
164218 ) from e
165219
220+ # Resolve runtime values from environment variables
221+ authorization_servers = config .authorization_servers
222+ if config .authorization_servers_env_var :
223+ env_value = os .environ .get (config .authorization_servers_env_var )
224+ if env_value :
225+ # Split comma-separated values and strip whitespace
226+ authorization_servers = [s .strip () for s in env_value .split ("," )]
227+
228+ resource_server_url = config .resource_server_url
229+ if config .resource_server_url_env_var :
230+ env_value = os .environ .get (config .resource_server_url_env_var )
231+ if env_value :
232+ resource_server_url = env_value
233+
166234 # Create the underlying token verifier
167235 token_verifier = create_auth_provider (config .token_verifier_config )
168236
@@ -172,8 +240,8 @@ def _create_remote_provider(config: RemoteAuthConfig) -> "AuthProvider":
172240
173241 return RemoteAuthProvider (
174242 token_verifier = token_verifier ,
175- authorization_servers = config . authorization_servers ,
176- resource_server_url = config . resource_server_url ,
243+ authorization_servers = authorization_servers ,
244+ resource_server_url = resource_server_url ,
177245 )
178246
179247
@@ -241,3 +309,25 @@ def create_dev_token_provider(
241309 required_scopes = required_scopes or [],
242310 )
243311 return _create_static_provider (config )
312+
313+
314+ def register_builtin_providers () -> None :
315+ """Register built-in authentication providers in the registry.
316+
317+ This function registers the standard Golf authentication providers:
318+ - jwt: JWT token verification
319+ - static: Static token verification (development)
320+ - oauth_server: Full OAuth authorization server
321+ - remote: Remote authorization server integration
322+ """
323+ registry = get_provider_registry ()
324+
325+ # Register built-in provider factories
326+ registry .register_factory ("jwt" , _create_jwt_provider )
327+ registry .register_factory ("static" , _create_static_provider )
328+ registry .register_factory ("oauth_server" , _create_oauth_server_provider )
329+ registry .register_factory ("remote" , _create_remote_provider )
330+
331+
332+ # Register built-in providers when module is imported
333+ register_builtin_providers ()
0 commit comments