1919from typing import Callable
2020
2121# Third-Party
22- from fastapi import Request , Response
22+ from fastapi .security import HTTPAuthorizationCredentials
23+ from starlette .requests import Request
24+ from starlette .responses import Response
2325from starlette .middleware .base import BaseHTTPMiddleware
2426
2527# First-Party
28+ from mcpgateway .auth import get_current_user
29+ from mcpgateway .db import SessionLocal
2630from mcpgateway .services .logging_service import LoggingService
2731from mcpgateway .services .structured_logger import get_structured_logger
2832from mcpgateway .utils .correlation_id import get_correlation_id
@@ -127,6 +131,44 @@ def __init__(self, app, enable_gateway_logging: bool = True, log_detailed_reques
127131 self .log_detailed_requests = log_detailed_requests
128132 self .log_level = log_level .upper ()
129133 self .max_body_size = max_body_size # Expected to be in bytes
134+
135+ async def _resolve_user_identity (self , request : Request ):
136+ """Best-effort extraction of user identity for request logs."""
137+ # Prefer context injected by upstream middleware
138+ if hasattr (request .state , "user" ) and request .state .user is not None :
139+ raw_user_id = getattr (request .state .user , "id" , None )
140+ user_email = getattr (request .state .user , "email" , None )
141+ return (str (raw_user_id ) if raw_user_id is not None else None , user_email )
142+
143+ # Fallback: try to authenticate using cookies/headers (matches AuthContextMiddleware)
144+ token = None
145+ if request .cookies :
146+ token = request .cookies .get ("jwt_token" ) or request .cookies .get ("access_token" ) or request .cookies .get ("token" )
147+
148+ if not token :
149+ auth_header = request .headers .get ("authorization" )
150+ if auth_header and auth_header .startswith ("Bearer " ):
151+ token = auth_header .replace ("Bearer " , "" )
152+
153+ if not token :
154+ return (None , None )
155+
156+ db = None
157+ try :
158+ db = SessionLocal ()
159+ credentials = HTTPAuthorizationCredentials (scheme = "Bearer" , credentials = token )
160+ user = await get_current_user (credentials , db )
161+ raw_user_id = getattr (user , "id" , None )
162+ user_email = getattr (user , "email" , None )
163+ return (str (raw_user_id ) if raw_user_id is not None else None , user_email )
164+ except Exception :
165+ return (None , None )
166+ finally :
167+ if db :
168+ try :
169+ db .close ()
170+ except Exception :
171+ pass
130172
131173 async def dispatch (self , request : Request , call_next : Callable ):
132174 """Process incoming request and log details with sensitive data masked.
@@ -147,6 +189,7 @@ async def dispatch(self, request: Request, call_next: Callable):
147189 method = request .method
148190 user_agent = request .headers .get ("user-agent" , "unknown" )
149191 client_ip = request .client .host if request .client else "unknown"
192+ user_id , user_email = await self ._resolve_user_identity (request )
150193
151194 # Skip boundary logging for health checks and static assets
152195 skip_paths = ["/health" , "/healthz" , "/static" , "/favicon.ico" ]
@@ -160,6 +203,8 @@ async def dispatch(self, request: Request, call_next: Callable):
160203 message = f"Request started: { method } { path } " ,
161204 component = "gateway" ,
162205 correlation_id = correlation_id ,
206+ user_email = user_email ,
207+ user_id = user_id ,
163208 operation_type = "http_request" ,
164209 request_method = method ,
165210 request_path = path ,
@@ -187,6 +232,8 @@ async def dispatch(self, request: Request, call_next: Callable):
187232 message = f"Request completed: { method } { path } - { response .status_code } " ,
188233 component = "gateway" ,
189234 correlation_id = correlation_id ,
235+ user_email = user_email ,
236+ user_id = user_id ,
190237 operation_type = "http_request" ,
191238 request_method = method ,
192239 request_path = path ,
@@ -295,6 +342,8 @@ async def receive():
295342 message = f"Request failed: { method } { path } " ,
296343 component = "gateway" ,
297344 correlation_id = correlation_id ,
345+ user_email = user_email ,
346+ user_id = user_id ,
298347 operation_type = "http_request" ,
299348 request_method = method ,
300349 request_path = path ,
@@ -324,6 +373,8 @@ async def receive():
324373 message = f"Request completed: { method } { path } - { status_code } " ,
325374 component = "gateway" ,
326375 correlation_id = correlation_id ,
376+ user_email = user_email ,
377+ user_id = user_id ,
327378 operation_type = "http_request" ,
328379 request_method = method ,
329380 request_path = path ,
0 commit comments