Spaces:
Sleeping
Sleeping
| """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" | |
| ) | |