diff --git a/api.py b/api.py index 23289b0..4f6a287 100644 --- a/api.py +++ b/api.py @@ -278,13 +278,12 @@ def get_auth_schemes_for_user(swagger_user: dict) -> dict: allowed_tags = swagger_user.get("allowed_tags") if not allowed_tags: - # Admin complet return { "HTTPBearer": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", - "description": "đŸŽ« Authentification JWT pour utilisateurs (POST /auth/login). " + "description": "Authentification JWT pour utilisateurs (POST /auth/login). " "Utilisez SOIT JWT SOIT API Key, pas les deux.", }, "ApiKeyAuth": { @@ -303,7 +302,7 @@ def get_auth_schemes_for_user(swagger_user: dict) -> dict: "type": "http", "scheme": "bearer", "bearerFormat": "JWT", - "description": "đŸŽ« Authentification JWT pour utilisateurs (POST /auth/login). " + "description": "Authentification JWT pour utilisateurs (POST /auth/login). " "Utilisez SOIT JWT SOIT API Key, pas les deux.", } @@ -461,9 +460,18 @@ async def custom_openapi_endpoint(request: Request): schema = generate_filtered_openapi_schema(app, allowed_tags, swagger_user) - if request.url.scheme == "https": - if "servers" not in schema or not schema["servers"]: - schema["servers"] = [{"url": str(request.base_url).rstrip("/")}] + scheme = request.url.scheme + forwarded_proto = request.headers.get("X-Forwarded-Proto") + + if forwarded_proto: + scheme = forwarded_proto + + base_url = str(request.base_url).rstrip("/") + + if scheme == "https" and base_url.startswith("http://"): + base_url = base_url.replace("http://", "https://", 1) + + schema["servers"] = [{"url": base_url}] return JSONResponse(content=schema) @@ -479,19 +487,31 @@ async def custom_swagger_ui(request: Request): headers={"WWW-Authenticate": 'Basic realm="Swagger UI"'}, ) + allowed_tags = swagger_user.get("allowed_tags") + is_restricted = allowed_tags is not None and len(allowed_tags) > 0 + + swagger_params = { + "persistAuthorization": True, + "displayRequestDuration": True, + "filter": True, + "tryItOutEnabled": True, + "docExpansion": "list", + } + + if is_restricted: + swagger_params["preAuthorizeApiKey"] = { + "ApiKeyAuth": { + "name": "X-API-Key", + "schema": {"type": "apiKey", "in": "header", "name": "X-API-Key"}, + "value": "", + } + } + return get_swagger_ui_html( openapi_url="/openapi.json", title=f"{app.title} - Documentation", swagger_favicon_url="https://fastapi.tiangolo.com/img/favicon.png", - swagger_ui_parameters={ - "persistAuthorization": True, - "displayRequestDuration": True, - "filter": True, - "tryItOutEnabled": True, - "docExpansion": "list", - # CORRECTIF : Ne pas prĂ©-remplir les credentials - "preAuthorizeApiKey": False, - }, + swagger_ui_parameters=swagger_params, ) diff --git a/middleware/security.py b/middleware/security.py index a1d0dda..0616595 100644 --- a/middleware/security.py +++ b/middleware/security.py @@ -158,18 +158,14 @@ class ApiKeyMiddlewareHTTP(BaseHTTPMiddleware): auth_header = request.headers.get("Authorization") api_key_header = request.headers.get("X-API-Key") - # CORRECTIF : Nettoyer et valider la clĂ© API if api_key_header: api_key_header = api_key_header.strip() - # Si la clĂ© est vide ou juste des espaces, la considĂ©rer comme absente if not api_key_header or api_key_header == "": api_key_header = None - # VĂ©rifier si c'est un token JWT Bearer if auth_header and auth_header.startswith("Bearer "): token = auth_header.split(" ", 1)[1].strip() - # CORRECTIF : VĂ©rifier si c'est une API Key envoyĂ©e par erreur dans Authorization if token.startswith("sdk_live_"): logger.warning( "⚠ API Key envoyĂ©e dans Authorization au lieu de X-API-Key" @@ -178,19 +174,16 @@ class ApiKeyMiddlewareHTTP(BaseHTTPMiddleware): request, token, path, method, call_next ) - # C'est un JWT valide, dĂ©lĂ©guer Ă  FastAPI - logger.debug(f"đŸŽ« JWT dĂ©tectĂ© pour {method} {path} → dĂ©lĂ©gation Ă  FastAPI") + logger.debug(f"JWT dĂ©tectĂ© pour {method} {path} → dĂ©lĂ©gation Ă  FastAPI") request.state.authenticated_via = "jwt" return await call_next(request) - # CORRECTIF : Si une clĂ© API est prĂ©sente ET non vide, la traiter if api_key_header: logger.debug(f" API Key dĂ©tectĂ©e pour {method} {path}") return await self._handle_api_key_auth( request, api_key_header, path, method, call_next ) - # Aucune authentification fournie, dĂ©lĂ©guer Ă  FastAPI qui renverra 401 logger.debug(f"❌ Aucune auth pour {method} {path} → dĂ©lĂ©gation Ă  FastAPI") return await call_next(request)