Improve auth flow

This commit is contained in:
Urtzi Alfaro
2025-07-19 17:49:03 +02:00
parent f3071c00bd
commit abc8b68ab4
16 changed files with 1437 additions and 572 deletions

View File

@@ -1,41 +1,76 @@
# shared/auth/decorators.py - NEW FILE
"""
Authentication decorators for FastAPI
Authentication decorators for microservices
"""
from functools import wraps
from fastapi import HTTPException, Depends
from fastapi.security import HTTPBearer
import httpx
import logging
from fastapi import HTTPException, status, Request
from typing import Callable, Optional
logger = logging.getLogger(__name__)
security = HTTPBearer()
def verify_service_token(auth_service_url: str):
"""Verify service token with auth service"""
def require_authentication(func: Callable) -> Callable:
"""Decorator to require authentication - assumes gateway has validated token"""
async def verify_token(token: str = Depends(security)):
try:
async with httpx.AsyncClient() as client:
response = await client.post(
f"{auth_service_url}/verify",
headers={"Authorization": f"Bearer {token.credentials}"}
)
if response.status_code == 200:
return response.json()
else:
raise HTTPException(
status_code=401,
detail="Invalid authentication credentials"
)
except httpx.RequestError as e:
logger.error(f"Auth service unavailable: {e}")
@wraps(func)
async def wrapper(*args, **kwargs):
# Find request object in arguments
request = None
for arg in args:
if isinstance(arg, Request):
request = arg
break
if not request:
# Check kwargs
request = kwargs.get('request')
if not request:
raise HTTPException(
status_code=503,
detail="Authentication service unavailable"
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Request object not found"
)
# Check if user context exists (set by gateway)
if not hasattr(request.state, 'user') or not request.state.user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Authentication required"
)
return await func(*args, **kwargs)
return verify_token
return wrapper
def require_tenant_access(func: Callable) -> Callable:
"""Decorator to require tenant access"""
@wraps(func)
async def wrapper(*args, **kwargs):
# Find request object
request = None
for arg in args:
if isinstance(arg, Request):
request = arg
break
if not request or not hasattr(request.state, 'tenant_id'):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Tenant access required"
)
return await func(*args, **kwargs)
return wrapper
def get_current_user(request: Request) -> dict:
"""Get current user from request state"""
if not hasattr(request.state, 'user'):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="User not authenticated"
)
return request.state.user
def get_current_tenant_id(request: Request) -> Optional[str]:
"""Get current tenant ID from request state"""
return getattr(request.state, 'tenant_id', None)