mashrur950 commited on
Commit
62ca1b3
·
1 Parent(s): fa14553

authentication addeed

Browse files
Files changed (2) hide show
  1. app.py +7 -0
  2. server.py +40 -1
app.py CHANGED
@@ -96,8 +96,15 @@ if __name__ == "__main__":
96
  try:
97
  # Add web routes for landing page and API key generation
98
  from starlette.responses import HTMLResponse, JSONResponse
 
99
  from database.api_keys import generate_api_key as db_generate_api_key
100
 
 
 
 
 
 
 
101
  @mcp.custom_route("/", methods=["GET"])
102
  async def landing_page(request):
103
  """Landing page with MCP connection information"""
 
96
  try:
97
  # Add web routes for landing page and API key generation
98
  from starlette.responses import HTMLResponse, JSONResponse
99
+ from starlette.requests import Request
100
  from database.api_keys import generate_api_key as db_generate_api_key
101
 
102
+ # NOTE: Custom middleware for FastMCP has known limitations (GitHub issue #817)
103
+ # Headers/state set in middleware are NOT accessible inside FastMCP tools.
104
+ # For local development, use SKIP_AUTH=true with ENV=development in .env
105
+ # For production, API key validation happens via extract_api_key_from_request() in server.py
106
+ logger.info("[Auth] Using FastMCP built-in request handling for authentication")
107
+
108
  @mcp.custom_route("/", methods=["GET"])
109
  async def landing_page(request):
110
  """Landing page with MCP connection information"""
server.py CHANGED
@@ -45,6 +45,25 @@ logger = logging.getLogger(__name__)
45
  # Store API key per request using context variable
46
  _current_api_key: ContextVar[str] = ContextVar('api_key', default=None)
47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  def get_api_key_from_context() -> str:
49
  """
50
  Get API key from the current request context.
@@ -56,15 +75,33 @@ def extract_api_key_from_request():
56
  """
57
  Extract API key from HTTP request and store in context variable.
58
  Called at the start of each tool execution.
 
 
 
 
59
  """
60
  try:
61
  from fastmcp.server.dependencies import get_http_request
62
  request = get_http_request()
 
 
63
  api_key = request.query_params.get('api_key')
64
  if api_key:
65
  _current_api_key.set(api_key)
66
  logger.debug(f"API key extracted from request: {api_key[:10]}...")
67
  return api_key
 
 
 
 
 
 
 
 
 
 
 
 
68
  except RuntimeError:
69
  # No HTTP request available (e.g., stdio transport)
70
  logger.debug("No HTTP request available for API key extraction")
@@ -124,7 +161,9 @@ def get_authenticated_user():
124
 
125
  # METHOD 3: Development bypass mode (local testing only)
126
  # SECURITY: Only allow SKIP_AUTH in development environments
127
- env = os.getenv("ENV", "production").lower()
 
 
128
  skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
129
 
130
  if skip_auth:
 
45
  # Store API key per request using context variable
46
  _current_api_key: ContextVar[str] = ContextVar('api_key', default=None)
47
 
48
+ # Session-to-API-key store: maps session_id -> api_key
49
+ # This allows tool calls to authenticate using the API key from the SSE connection
50
+ _session_auth_store: dict[str, str] = {}
51
+
52
+ def store_session_api_key(session_id: str, api_key: str):
53
+ """Store API key for a session (called when SSE connection is made)"""
54
+ _session_auth_store[session_id] = api_key
55
+ logger.debug(f"Stored API key for session {session_id[:16]}...")
56
+
57
+ def get_api_key_from_session(session_id: str) -> str:
58
+ """Get API key from session store"""
59
+ return _session_auth_store.get(session_id)
60
+
61
+ def cleanup_session(session_id: str):
62
+ """Remove session from store when disconnected"""
63
+ if session_id in _session_auth_store:
64
+ del _session_auth_store[session_id]
65
+ logger.debug(f"Cleaned up session {session_id[:16]}...")
66
+
67
  def get_api_key_from_context() -> str:
68
  """
69
  Get API key from the current request context.
 
75
  """
76
  Extract API key from HTTP request and store in context variable.
77
  Called at the start of each tool execution.
78
+
79
+ Checks multiple sources:
80
+ 1. api_key query parameter in current request
81
+ 2. session_id query parameter -> lookup in session store
82
  """
83
  try:
84
  from fastmcp.server.dependencies import get_http_request
85
  request = get_http_request()
86
+
87
+ # METHOD 1: Direct api_key in query params
88
  api_key = request.query_params.get('api_key')
89
  if api_key:
90
  _current_api_key.set(api_key)
91
  logger.debug(f"API key extracted from request: {api_key[:10]}...")
92
  return api_key
93
+
94
+ # METHOD 2: Look up by session_id (for MCP tool calls)
95
+ session_id = request.query_params.get('session_id')
96
+ if session_id:
97
+ api_key = get_api_key_from_session(session_id)
98
+ if api_key:
99
+ _current_api_key.set(api_key)
100
+ logger.debug(f"API key found via session {session_id[:16]}...")
101
+ return api_key
102
+ else:
103
+ logger.debug(f"No API key found for session {session_id[:16]}...")
104
+
105
  except RuntimeError:
106
  # No HTTP request available (e.g., stdio transport)
107
  logger.debug("No HTTP request available for API key extraction")
 
161
 
162
  # METHOD 3: Development bypass mode (local testing only)
163
  # SECURITY: Only allow SKIP_AUTH in development environments
164
+ # Check both ENV and ENVIRONMENT variables for compatibility
165
+ env = os.getenv("ENV") or os.getenv("ENVIRONMENT", "production")
166
+ env = env.lower()
167
  skip_auth = os.getenv("SKIP_AUTH", "false").lower() == "true"
168
 
169
  if skip_auth: