import jwt import requests from fastapi import HTTPException, Security, Depends from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from supabase_client import supabase_db from config import SUPABASE_URL, SUPABASE_JWT_SECRET import logging logger = logging.getLogger(__name__) security = HTTPBearer() # Cache for JWKS client _jwks_client = None def get_jwks_client(): global _jwks_client if _jwks_client is None: jwks_url = f"{SUPABASE_URL}/auth/v1/.well-known/jwks.json" _jwks_client = jwt.PyJWKClient(jwks_url) return _jwks_client async def get_current_user(credentials: HTTPAuthorizationCredentials = Security(security)): token = credentials.credentials try: # First attempt: decode using HS256 with the JWT secret try: payload = jwt.decode( token, SUPABASE_JWT_SECRET, algorithms=["HS256"], audience="authenticated" ) logger.info("Token verified with HS256") except jwt.InvalidTokenError as hs_error: # If HS256 fails, try RS256/ES256 using the JWKS logger.info(f"HS256 failed: {hs_error}, trying JWKS...") jwks_client = get_jwks_client() signing_key = jwks_client.get_signing_key_from_jwt(token) payload = jwt.decode( token, signing_key.key, algorithms=["RS256", "ES256"], audience="authenticated", options={"verify_aud": True} ) logger.info("Token verified with JWKS (RS256/ES256)") user_id = payload.get("sub") if not user_id: raise HTTPException(status_code=401, detail="Invalid token: missing subject") # Fetch user profile from Supabase (service client bypasses RLS) profile = supabase_db.table("profiles").select("*").eq("id", user_id).execute() if not profile.data: raise HTTPException(status_code=404, detail="User profile not found") return { "id": user_id, "email": payload.get("email"), "profile": profile.data[0] } except jwt.ExpiredSignatureError: raise HTTPException(status_code=401, detail="Token expired") except jwt.InvalidTokenError as e: logger.error(f"Token decode error: {e}") raise HTTPException(status_code=401, detail=f"Invalid authentication: {e}") except Exception as e: logger.error(f"Unexpected error in auth: {e}") raise HTTPException(status_code=401, detail="Authentication failed")