fix(security): improve api key and jwt validation handling

This commit is contained in:
Fanilo-Nantenaina 2026-01-21 14:00:57 +03:00
parent 574d82f3c4
commit 1c6c45465f
2 changed files with 21 additions and 7 deletions

17
api.py
View file

@ -278,18 +278,21 @@ 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": {
"type": "apiKey",
"in": "header",
"name": "X-API-Key",
"description": "Clé API pour intégrations externes (format: sdk_live_xxx)",
"description": " Clé API pour intégrations externes (format: sdk_live_xxx). "
"Utilisez SOIT JWT SOIT API Key, pas les deux.",
},
}
@ -300,14 +303,16 @@ 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.",
}
schemes["ApiKeyAuth"] = {
"type": "apiKey",
"in": "header",
"name": "X-API-Key",
"description": "Clé API pour intégrations externes (format: sdk_live_xxx)",
"description": " Clé API pour intégrations externes (format: sdk_live_xxx). "
"Utilisez SOIT JWT SOIT API Key, pas les deux.",
}
return schemes
@ -483,7 +488,9 @@ async def custom_swagger_ui(request: Request):
"displayRequestDuration": True,
"filter": True,
"tryItOutEnabled": True,
"docExpansion": "list", # Meilleure UX
"docExpansion": "list",
# CORRECTIF : Ne pas pré-remplir les credentials
"preAuthorizeApiKey": False,
},
)

View file

@ -158,14 +158,18 @@ 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].strip()
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"
@ -174,16 +178,19 @@ 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")
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}")
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)