Coverage for /Users/antonigmitruk/golf/src/golf/core/builder_auth.py: 0%
32 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-08-16 18:46 +0200
« prev ^ index » next coverage.py v7.6.12, created at 2025-08-16 18:46 +0200
1"""Authentication integration for the Golf MCP build process.
3This module adds support for injecting authentication configuration
4into the generated FastMCP application during the build process using
5FastMCP 2.11+ built-in auth providers.
6"""
8from golf.auth import get_auth_config, is_auth_configured
9from golf.auth.api_key import get_api_key_config
10from golf.auth.providers import AuthConfig
13def generate_auth_code(
14 server_name: str,
15 host: str = "localhost",
16 port: int = 3000,
17 https: bool = False,
18 opentelemetry_enabled: bool = False,
19 transport: str = "streamable-http",
20) -> dict:
21 """Generate authentication components for the FastMCP app using modern
22 auth providers.
24 Returns a dictionary with:
25 - imports: List of import statements
26 - setup_code: Auth setup code (provider configuration, etc.)
27 - fastmcp_args: Dict of arguments to add to FastMCP constructor
28 - has_auth: Whether auth is configured
29 """
30 # Check for API key configuration first
31 api_key_config = get_api_key_config()
32 if api_key_config:
33 return generate_api_key_auth_components(server_name, opentelemetry_enabled, transport)
35 # Check for modern auth configuration
36 auth_config = get_auth_config()
37 if not auth_config:
38 # If no auth config, return empty components
39 return {"imports": [], "setup_code": [], "fastmcp_args": {}, "has_auth": False}
41 # Validate that we have a modern auth config
42 if not isinstance(auth_config, AuthConfig):
43 raise ValueError(
44 f"Invalid auth configuration type: {type(auth_config).__name__}. "
45 "Golf 0.2.x requires modern auth configurations (JWTAuthConfig, "
46 "StaticTokenConfig, OAuthServerConfig, or RemoteAuthConfig). "
47 "Please update your auth.py file."
48 )
50 # Generate modern auth components with embedded configuration
51 auth_imports = [
52 "import os",
53 "import sys",
54 "from golf.auth.factory import create_auth_provider",
55 "from golf.auth.providers import RemoteAuthConfig, JWTAuthConfig, StaticTokenConfig, OAuthServerConfig",
56 ]
58 # Embed the auth configuration directly in the generated code
59 # Convert the auth config to its string representation for embedding
60 auth_config_repr = repr(auth_config)
62 setup_code_lines = [
63 "# Modern FastMCP 2.11+ authentication setup with embedded configuration",
64 f"auth_config = {auth_config_repr}",
65 "try:",
66 " auth_provider = create_auth_provider(auth_config)",
67 " print(f'Authentication configured with {auth_config.provider_type} provider')",
68 "except Exception as e:",
69 " print(f'Authentication setup failed: {e}', file=sys.stderr)",
70 " auth_provider = None",
71 "",
72 ]
74 # FastMCP constructor arguments - FastMCP 2.11+ uses auth parameter
75 fastmcp_args = {"auth": "auth_provider"}
77 return {
78 "imports": auth_imports,
79 "setup_code": setup_code_lines,
80 "fastmcp_args": fastmcp_args,
81 "has_auth": True,
82 }
85def generate_api_key_auth_components(
86 server_name: str,
87 opentelemetry_enabled: bool = False,
88 transport: str = "streamable-http",
89) -> dict:
90 """Generate authentication components for API key authentication.
92 Returns a dictionary with:
93 - imports: List of import statements
94 - setup_code: Auth setup code (middleware setup)
95 - fastmcp_args: Dict of arguments to add to FastMCP constructor
96 - has_auth: Whether auth is configured
97 """
98 api_key_config = get_api_key_config()
99 if not api_key_config:
100 return {"imports": [], "setup_code": [], "fastmcp_args": {}, "has_auth": False}
102 auth_imports = [
103 "# API key authentication setup",
104 "from golf.auth.api_key import get_api_key_config, configure_api_key",
105 "from golf.auth import set_api_key",
106 "from starlette.middleware.base import BaseHTTPMiddleware",
107 "from starlette.requests import Request",
108 "from starlette.responses import JSONResponse",
109 "import os",
110 ]
112 setup_code_lines = [
113 "# Recreate API key configuration from auth.py",
114 "configure_api_key(",
115 f" header_name={repr(api_key_config.header_name)},",
116 f" header_prefix={repr(api_key_config.header_prefix)},",
117 f" required={repr(api_key_config.required)}",
118 ")",
119 "",
120 "# Simplified API key middleware that validates presence",
121 "class ApiKeyMiddleware(BaseHTTPMiddleware):",
122 " async def dispatch(self, request: Request, call_next):",
123 " # Debug mode from environment",
124 " debug = os.environ.get('GOLF_API_KEY_DEBUG', '').lower() == 'true'",
125 " ",
126 " # Skip auth for monitoring endpoints",
127 " path = request.url.path",
128 " if path in ['/metrics', '/health']:",
129 " return await call_next(request)",
130 " ",
131 " api_key_config = get_api_key_config()",
132 " ",
133 " if api_key_config:",
134 " # Extract API key from the configured header",
135 " header_name = api_key_config.header_name",
136 " header_prefix = api_key_config.header_prefix",
137 " ",
138 " # Case-insensitive header lookup",
139 " api_key = None",
140 " for k, v in request.headers.items():",
141 " if k.lower() == header_name.lower():",
142 " api_key = v",
143 " break",
144 " ",
145 " # Process the API key if found",
146 " if api_key:",
147 " # Strip prefix if configured",
148 " if header_prefix and api_key.startswith(header_prefix):",
149 " api_key = api_key[len(header_prefix):]",
150 " ",
151 " # Store the API key in request state for tools to access",
152 " request.state.api_key = api_key",
153 " ",
154 " # Also store in context variable for tools",
155 " set_api_key(api_key)",
156 " ",
157 " # Check if API key is required but missing",
158 " if api_key_config.required and not api_key:",
159 " return JSONResponse(",
160 " {'error': 'unauthorized', "
161 "'detail': f'Missing required {header_name} header'},"
162 " status_code=401,",
163 " headers={'WWW-Authenticate': f'{header_name} realm=\"MCP Server\"'}",
164 " )",
165 " ",
166 " # Continue with the request",
167 " return await call_next(request)",
168 "",
169 ]
171 # API key auth is handled via middleware, not FastMCP constructor args
172 fastmcp_args = {}
174 return {
175 "imports": auth_imports,
176 "setup_code": setup_code_lines,
177 "fastmcp_args": fastmcp_args,
178 "has_auth": True,
179 }
182def generate_auth_routes() -> str:
183 """Generate code for auth routes in the FastMCP app.
185 Auth providers (RemoteAuthProvider, OAuthProvider) provide OAuth metadata routes
186 that need to be added to the server.
187 """
188 # API key auth doesn't need special routes
189 api_key_config = get_api_key_config()
190 if api_key_config:
191 return ""
193 # Check if auth is configured
194 if not is_auth_configured():
195 return ""
197 # Auth providers provide OAuth metadata routes that need to be added to the server
198 return """
199# Add OAuth metadata routes from auth provider
200if auth_provider and hasattr(auth_provider, 'get_routes'):
201 auth_routes = auth_provider.get_routes()
202 if auth_routes:
203 # Add routes to FastMCP's additional HTTP routes list
204 try:
205 mcp._additional_http_routes.extend(auth_routes)
206 print(f"Added {len(auth_routes)} OAuth metadata routes")
207 except Exception as e:
208 print(f"Warning: Failed to add OAuth routes: {e}")
209"""