"""Human requests use user identities; tokens are reserved for scoped machines.""" import hmac import os from ipaddress import ip_address, ip_network from fastapi import Header, HTTPException, Request from .config import settings def token_is_strong(value: str | None, *, minimum_length: int = 24) -> bool: return bool(value and len(value) >= minimum_length and value.strip().lower() not in {"change-me", "changeme", "dev", "test", "dev-voice-token"}) def _extract_bearer_token(authorization: str | None) -> str: if not authorization or not authorization.startswith("Bearer "): raise HTTPException(401, "Authorization header missing") return authorization[len("Bearer "):] def require_auth_token(provided_token: str | None, expected_token: str | None, scope: str): if not expected_token: raise HTTPException(503, f"{scope} authentication is not configured") if not hmac.compare_digest(_extract_bearer_token(provided_token), expected_token): raise HTTPException(403, f"Invalid {scope} token") def _request_from_trusted_operator_proxy(request: Request) -> bool: if not settings.operator_proxy_auth_enabled or not settings.operator_proxy_trusted_networks: return False try: client_ip = ip_address(request.client.host if request.client else "") except ValueError: return False for configured_network in settings.operator_proxy_trusted_networks: try: if client_ip in ip_network(configured_network, strict=False): return True except ValueError: continue return False def require_operator_request(request: Request) -> str: # Compatibility for existing governed routes; no shared token or unlock. from ..services.user_access import authorize_request user = authorize_request(request) return f"{user.provider}:{user.subject}" def require_service_request(request: Request, service: str) -> str: if service == "mcp": enabled, expected = settings.mcp_enabled, settings.mcp_token elif service == "n8n": enabled, expected = settings.n8n_enabled, settings.n8n_token elif service == "voice": enabled, expected = True, settings.voice_token or os.getenv("APP_OPS_VOICE_TOKEN") else: raise HTTPException(403, "Unknown service identity") if not enabled: raise HTTPException(404, f"{service} integration is disabled") if not expected or ((service != 'voice' or settings.environment == 'production') and not token_is_strong(expected)): raise HTTPException(503, f"{service} authentication is not safely configured") require_auth_token(request.headers.get("Authorization"), expected, service) return service def n8n_auth(request: Request) -> str: return require_service_request(request, "n8n") def operator_auth(request: Request) -> str: return require_operator_request(request) def voice_auth(request: Request, authorization: str | None = Header(default=None, alias="Authorization")): if authorization: expected = settings.voice_token or os.getenv("APP_OPS_VOICE_TOKEN") require_auth_token(authorization, expected, "voice") else: require_operator_request(request) return True