- Pass service registry to load balancer for dependency injection - Remove dynamic imports of service registry in load balancer - Update service registration and health check logic - Enable token-service in docker-compose and service config - Add room names and rooms proxy endpoints - Improve logging for proxy requests and health checks - Update deploy script project name to sa4cps - Add test script for coroutine fix - Minor code cleanup and formatting
91 lines
3.5 KiB
Python
91 lines
3.5 KiB
Python
"""
|
|
Authentication middleware for API Gateway
|
|
"""
|
|
|
|
import aiohttp
|
|
from fastapi import HTTPException, Request
|
|
from typing import Optional, Dict, Any
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class AuthMiddleware:
|
|
"""Authentication middleware for validating tokens"""
|
|
|
|
def __init__(self, token_service_url: str = "http://localhost:8001"):
|
|
self.token_service_url = token_service_url
|
|
logger.info(f"Initialized AuthMiddleware with token service URL: {self.token_service_url}")
|
|
|
|
async def verify_token(self, request: Request) -> Optional[Dict[str, Any]]:
|
|
"""
|
|
Verify authentication token from request headers
|
|
Returns token payload if valid, raises HTTPException if invalid
|
|
"""
|
|
# Extract token from Authorization header
|
|
auth_header = request.headers.get("Authorization")
|
|
if not auth_header:
|
|
raise HTTPException(status_code=401, detail="Authorization header required")
|
|
|
|
if not auth_header.startswith("Bearer "):
|
|
raise HTTPException(status_code=401, detail="Bearer token required")
|
|
|
|
token = auth_header[7:] # Remove "Bearer " prefix
|
|
|
|
try:
|
|
# Validate token with token service
|
|
async with aiohttp.ClientSession() as session:
|
|
async with session.post(
|
|
f"{self.token_service_url}/tokens/validate",
|
|
json={"token": token},
|
|
timeout=aiohttp.ClientTimeout(total=5)
|
|
) as response:
|
|
|
|
if response.status != 200:
|
|
raise HTTPException(status_code=401, detail="Token validation failed")
|
|
|
|
token_data = await response.json()
|
|
|
|
if not token_data.get("valid"):
|
|
error_msg = token_data.get("error", "Invalid token")
|
|
raise HTTPException(status_code=401, detail=error_msg)
|
|
|
|
# Token is valid, return decoded payload
|
|
return token_data.get("decoded")
|
|
|
|
except aiohttp.ClientError as e:
|
|
logger.error(f"Token service connection error: {e}")
|
|
raise HTTPException(status_code=503, detail="Authentication service unavailable")
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Token verification error: {e}")
|
|
raise HTTPException(status_code=500, detail="Authentication error")
|
|
|
|
async def check_permissions(self, token_payload: Dict[str, Any], required_resources: list) -> bool:
|
|
"""
|
|
Check if token has required permissions for specific resources
|
|
"""
|
|
if not token_payload:
|
|
return False
|
|
|
|
# Get list of resources the token has access to
|
|
token_resources = token_payload.get("list_of_resources", [])
|
|
|
|
# Check if token has access to all required resources
|
|
for resource in required_resources:
|
|
if resource not in token_resources:
|
|
return False
|
|
|
|
return True
|
|
|
|
def extract_user_info(self, token_payload: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""Extract user information from token payload"""
|
|
return {
|
|
"name": token_payload.get("name"),
|
|
"resources": token_payload.get("list_of_resources", []),
|
|
"data_aggregation": token_payload.get("data_aggregation", False),
|
|
"time_aggregation": token_payload.get("time_aggregation", False),
|
|
"embargo": token_payload.get("embargo", 0),
|
|
"expires_at": token_payload.get("exp")
|
|
}
|