82 lines
2.7 KiB
Python
82 lines
2.7 KiB
Python
import time
|
|
import structlog
|
|
from typing import Dict, Any
|
|
from shared.auth.jwt_handler import JWTHandler
|
|
from app.core.config import settings
|
|
|
|
logger = structlog.get_logger()
|
|
|
|
class ServiceAuthenticator:
|
|
"""Handles service-to-service authentication via gateway"""
|
|
|
|
def __init__(self):
|
|
self.jwt_handler = JWTHandler(settings.JWT_SECRET_KEY)
|
|
self._cached_token = None
|
|
self._token_expires_at = 0
|
|
|
|
async def get_service_token(self) -> str:
|
|
"""
|
|
Get a valid service token, using cache when possible
|
|
Creates JWT tokens that the gateway will accept
|
|
"""
|
|
current_time = int(time.time())
|
|
|
|
# Return cached token if still valid (with 5 min buffer)
|
|
if (self._cached_token and
|
|
self._token_expires_at > current_time + 300):
|
|
return self._cached_token
|
|
|
|
# Create new service token
|
|
token_expires_at = current_time + 3600 # 1 hour
|
|
|
|
service_payload = {
|
|
# ✅ Required fields for gateway middleware
|
|
"sub": "training-service",
|
|
"user_id": "training-service",
|
|
"email": "training-service@internal",
|
|
"type": "access", # ✅ Must be "access" for gateway
|
|
|
|
# ✅ Expiration and timing
|
|
"exp": token_expires_at,
|
|
"iat": current_time,
|
|
"iss": "training-service",
|
|
|
|
# ✅ Service identification
|
|
"service": "training",
|
|
"full_name": "Training Service",
|
|
"is_verified": True,
|
|
"is_active": True,
|
|
|
|
# ✅ Optional tenant context (can be overridden per request)
|
|
"tenant_id": None
|
|
}
|
|
|
|
try:
|
|
token = self.jwt_handler.create_access_token_from_payload(service_payload)
|
|
|
|
# Cache the token
|
|
self._cached_token = token
|
|
self._token_expires_at = token_expires_at
|
|
|
|
logger.debug("Created new service token", expires_at=token_expires_at)
|
|
return token
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to create service token: {e}")
|
|
raise ValueError(f"Service token creation failed: {e}")
|
|
|
|
def get_request_headers(self, tenant_id: str = None) -> Dict[str, str]:
|
|
"""Get standard headers for service requests"""
|
|
headers = {
|
|
"Content-Type": "application/json",
|
|
"X-Service": "training-service",
|
|
"User-Agent": "training-service/1.0.0"
|
|
}
|
|
|
|
if tenant_id:
|
|
headers["X-Tenant-ID"] = str(tenant_id)
|
|
|
|
return headers
|
|
|
|
# Global authenticator instance
|
|
service_auth = ServiceAuthenticator() |