"""Authentication with Clerk.""" from fastapi import Request, HTTPException, status, Depends from sqlalchemy.orm import Session from app.models import User from app.config import get_settings from app.database import get_db import httpx import logging import jwt from jwt import PyJWKClient logger = logging.getLogger(__name__) settings = get_settings() # Initialize JWKS client for token verification jwks_client = PyJWKClient(settings.CLERK_JWKS_URL) async def get_current_user( request: Request, db: Session = Depends(get_db) ) -> User: """Get current authenticated user from Clerk token.""" # Extract Bearer token auth_header = request.headers.get("Authorization") if not auth_header or not auth_header.startswith("Bearer "): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Missing authentication" ) token = auth_header.replace("Bearer ", "") try: # Get signing key from JWKS signing_key = jwks_client.get_signing_key_from_jwt(token) # Verify and decode token payload = jwt.decode( token, signing_key.key, algorithms=["RS256"], options={"verify_exp": True} ) # Extract user info from token clerk_user_id = payload.get("sub") if not clerk_user_id: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token data" ) # Get user details from Clerk API async with httpx.AsyncClient() as client: response = await client.get( f"{settings.CLERK_API_URL}/users/{clerk_user_id}", headers={ "Authorization": f"Bearer {settings.CLERK_SECRET_KEY}" } ) if response.status_code != 200: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Failed to fetch user data" ) user_data = response.json() # Extract email and org email = None if user_data.get("email_addresses"): email = user_data["email_addresses"][0].get("email_address") org_id = user_data.get("organization_id", "default") if not email: email = f"{clerk_user_id}@clerk.user" # Get or create user in database user = db.query(User).filter(User.clerk_user_id == clerk_user_id).first() if not user: user = User( clerk_user_id=clerk_user_id, email=email, org_id=org_id ) db.add(user) db.commit() db.refresh(user) logger.info(f"Created new user: {email}") return user except jwt.ExpiredSignatureError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Token has expired" ) except jwt.InvalidTokenError as e: logger.error(f"Invalid token: {e}") raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token" ) except HTTPException: raise except Exception as e: logger.error(f"Authentication error: {e}") raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication failed" )