Fix token issue

This commit is contained in:
Urtzi Alfaro
2025-07-18 16:48:49 +02:00
parent 4073222888
commit e92ccb8e0a
8 changed files with 235 additions and 74 deletions

View File

@@ -10,7 +10,7 @@ import uuid
from datetime import datetime
from app.core.database import get_db
from app.core.auth import verify_token
from app.core.auth import get_current_user, AuthInfo
from app.services.sales_service import SalesService
from app.services.data_import_service import DataImportService
from app.services.messaging import data_publisher
@@ -27,7 +27,7 @@ router = APIRouter()
async def create_sales_record(
sales_data: SalesDataCreate,
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(verify_token)
current_user: AuthInfo = Depends(get_current_user)
):
"""Create a new sales record"""
try:
@@ -49,7 +49,7 @@ async def create_sales_record(
async def get_sales_data(
query: SalesDataQuery,
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(verify_token)
current_user: AuthInfo = Depends(get_current_user)
):
"""Get sales data by query parameters"""
try:
@@ -64,7 +64,7 @@ async def import_sales_data(
file_format: str = Form(...),
file: UploadFile = File(...),
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(verify_token)
current_user: AuthInfo = Depends(get_current_user)
):
"""Import sales data from file"""
try:
@@ -96,7 +96,7 @@ async def import_sales_data(
async def import_sales_json(
import_data: SalesDataImport,
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(verify_token)
current_user: AuthInfo = Depends(get_current_user)
):
"""Import sales data from JSON"""
try:
@@ -123,7 +123,7 @@ async def import_sales_json(
@router.post("/import/validate")
async def validate_import_data(
import_data: SalesDataImport,
current_user: dict = Depends(verify_token)
current_user: AuthInfo = Depends(get_current_user)
):
"""Validate import data before processing"""
try:
@@ -138,7 +138,7 @@ async def validate_import_data(
@router.get("/import/template/{format_type}")
async def get_import_template(
format_type: str,
current_user: dict = Depends(verify_token)
current_user: AuthInfo = Depends(get_current_user)
):
"""Get import template for specified format"""
try:
@@ -178,7 +178,7 @@ async def import_sales_data_advanced(
file: UploadFile = File(...),
validate_only: bool = Form(False),
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(verify_token)
current_user: AuthInfo = Depends(get_current_user)
):
"""Advanced import with validation and preview options"""
try:
@@ -239,7 +239,7 @@ async def get_import_history(
limit: int = Query(10, ge=1, le=100),
offset: int = Query(0, ge=0),
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(verify_token)
current_user: AuthInfo = Depends(get_current_user)
):
"""Get import history for tenant"""
try:
@@ -292,7 +292,7 @@ async def delete_import_batch(
import_date: str, # Format: YYYY-MM-DD
source: str = Query(..., description="Import source (csv, excel, json, pos)"),
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(verify_token)
current_user: AuthInfo = Depends(get_current_user)
):
"""Delete an entire import batch"""
try:
@@ -354,7 +354,7 @@ async def get_sales_statistics(
start_date: datetime = Query(None, description="Start date for statistics"),
end_date: datetime = Query(None, description="End date for statistics"),
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(verify_token)
current_user: AuthInfo = Depends(get_current_user)
):
"""Get sales statistics for tenant"""
try:
@@ -454,7 +454,7 @@ async def export_sales_data(
end_date: datetime = Query(None, description="End date"),
products: List[str] = Query(None, description="Filter by products"),
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(verify_token)
current_user: AuthInfo = Depends(get_current_user)
):
"""Export sales data in specified format"""
try:

View File

@@ -9,7 +9,7 @@ from typing import List, Optional
from datetime import datetime, timedelta
from app.core.database import get_db
from app.core.auth import verify_token
from app.core.auth import get_current_user, AuthInfo
from app.services.traffic_service import TrafficService
from app.services.messaging import data_publisher
from app.schemas.external import (
@@ -25,7 +25,7 @@ traffic_service = TrafficService()
async def get_current_traffic(
latitude: float = Query(..., description="Latitude"),
longitude: float = Query(..., description="Longitude"),
current_user: dict = Depends(verify_token)
current_user: AuthInfo = Depends(get_current_user)
):
"""Get current traffic data for location"""
try:
@@ -52,7 +52,7 @@ async def get_historical_traffic(
start_date: datetime = Query(..., description="Start date"),
end_date: datetime = Query(..., description="End date"),
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(verify_token)
current_user: AuthInfo = Depends(get_current_user)
):
"""Get historical traffic data"""
try:

View File

@@ -9,7 +9,7 @@ from typing import List, Optional
from datetime import datetime, timedelta
from app.core.database import get_db
from app.core.auth import verify_token
from app.core.auth import get_current_user, AuthInfo
from app.services.weather_service import WeatherService
from app.services.messaging import data_publisher
from app.schemas.external import (
@@ -26,7 +26,7 @@ weather_service = WeatherService()
async def get_current_weather(
latitude: float = Query(..., description="Latitude"),
longitude: float = Query(..., description="Longitude"),
current_user: dict = Depends(verify_token)
current_user: AuthInfo = Depends(get_current_user)
):
"""Get current weather for location"""
try:
@@ -43,7 +43,7 @@ async def get_weather_forecast(
latitude: float = Query(..., description="Latitude"),
longitude: float = Query(..., description="Longitude"),
days: int = Query(7, description="Number of forecast days", ge=1, le=14),
current_user: dict = Depends(verify_token)
current_user: AuthInfo = Depends(get_current_user)
):
"""Get weather forecast for location"""
try:
@@ -69,7 +69,7 @@ async def get_historical_weather(
start_date: datetime = Query(..., description="Start date"),
end_date: datetime = Query(..., description="End date"),
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(verify_token)
current_user: AuthInfo = Depends(get_current_user)
):
"""Get historical weather data"""
try:

View File

@@ -1,35 +1,71 @@
# ================================================================
# services/data/app/core/auth.py
# ================================================================
"""Authentication utilities for data service"""
from fastapi import HTTPException, Depends, status
from fastapi import HTTPException, Depends, status, Request
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import httpx
import structlog
from typing import Dict, Any, Optional
from app.core.config import settings
logger = structlog.get_logger()
security = HTTPBearer()
security = HTTPBearer(auto_error=False) # ✅ Don't auto-error, we'll handle manually
async def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)) -> dict:
"""Verify JWT token with auth service"""
class AuthInfo:
"""Authentication information"""
def __init__(self, user_id: str, email: str, tenant_id: str, roles: list):
self.user_id = user_id
self.email = email
self.tenant_id = tenant_id
self.roles = roles
async def get_current_user(
request: Request,
credentials: Optional[HTTPAuthorizationCredentials] = Depends(security)
) -> AuthInfo:
"""Get current user from gateway headers or token verification"""
# ✅ OPTION 1: Check for gateway headers (preferred when using gateway)
user_id = request.headers.get("X-User-ID")
email = request.headers.get("X-User-Email")
tenant_id = request.headers.get("X-Tenant-ID")
roles_header = request.headers.get("X-User-Roles", "")
if user_id and email and tenant_id:
# Gateway already authenticated the user
roles = roles_header.split(",") if roles_header else ["user"]
logger.info("Authenticated via gateway headers", user_id=user_id, email=email)
return AuthInfo(user_id, email, tenant_id, roles)
# ✅ OPTION 2: Direct token verification (when not using gateway)
if not credentials:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Authentication required (no token or gateway headers)"
)
try:
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(timeout=5.0) as client:
response = await client.post(
f"{settings.AUTH_SERVICE_URL}/api/v1/auth/verify",
headers={"Authorization": f"Bearer {credentials.credentials}"}
)
if response.status_code == 200:
return response.json()
user_data = response.json()
logger.info("Authenticated via direct token", user_id=user_data.get("user_id"))
return AuthInfo(
user_id=user_data["user_id"],
email=user_data["email"],
tenant_id=user_data["tenant_id"],
roles=user_data.get("roles", ["user"])
)
else:
logger.warning("Token verification failed", status_code=response.status_code)
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials"
)
except httpx.RequestError:
except httpx.RequestError as e:
logger.error("Auth service unavailable", error=str(e))
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Authentication service unavailable"