diff --git a/backend/auth/jwt.py b/backend/auth/jwt.py index fa4fd9e..fcf6b20 100644 --- a/backend/auth/jwt.py +++ b/backend/auth/jwt.py @@ -1,40 +1,63 @@ -# backend/auth/jwt.py +# auth/jwt.py -import requests -from jose import jwt -from jose.exceptions import JWTError -from fastapi import Depends, HTTPException, status -from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials -from typing import Dict +from fastapi import Request, HTTPException, status, Depends +from jose import jwt, JWTError +import os -SUPABASE_PROJECT_ID = "lcoretjgpauozmuoedus" # <-- Replace with your project ref -SUPABASE_JWKS_URL = f"https://{SUPABASE_PROJECT_ID}.supabase.co/auth/v1/keys" +JWT_SECRET = os.getenv("SUPABASE_JWT_SECRET") +ALGORITHM = "HS256" -# Fetch JWKs from Supabase -jwks = requests.get(SUPABASE_JWKS_URL).json() -# Define FastAPI's bearer auth scheme -auth_scheme = HTTPBearer() +def get_token_from_header(request: Request) -> str: + 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 or invalid Authorization header", + ) + return auth_header.split(" ")[1] -# Decode + verify JWT token -def verify_jwt_token(token: str) -> Dict: + +# def verify_jwt_token(token: str) -> dict: +# try: +# payload = jwt.decode(token, JWT_SECRET, algorithms=[ALGORITHM]) +# return payload +# except JWTError as e: +# raise HTTPException( +# status_code=status.HTTP_403_FORBIDDEN, +# detail=f"Invalid JWT token: {str(e)}", +# ) +def verify_jwt_token(token: str) -> dict: try: - header = jwt.get_unverified_header(token) - kid = header["kid"] + payload = jwt.decode( + token, + JWT_SECRET, + algorithms=[ALGORITHM], + options={"verify_aud": False} # <- Disable audience verification + ) - key = next((k for k in jwks["keys"] if k["kid"] == kid), None) - if key is None: - raise HTTPException(status_code=403, detail="Invalid Supabase JWT: No matching key") - - payload = jwt.decode(token, key, algorithms=["RS256"], options={"verify_aud": False}) return payload - except JWTError as e: - raise HTTPException(status_code=403, detail=f"Invalid Supabase JWT: {str(e)}") + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=f"Invalid JWT token: {str(e)}", + ) -# Dependency for protected endpoints -def get_current_user( - credentials: HTTPAuthorizationCredentials = Depends(auth_scheme) -) -> Dict: - token = credentials.credentials - return verify_jwt_token(token) + +def get_current_user(request: Request): + token = get_token_from_header(request) + payload = verify_jwt_token(token) + + user_id = payload.get("sub") + if not user_id: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="JWT missing 'sub' field", + ) + print("🔐 JWT payload seen by API →", payload) + return { + "id": user_id, + "email": payload.get("email"), + "role": payload.get("role"), + "tenant_id": payload.get("tenant_id", "unknown"), + } diff --git a/backend/main.py b/backend/main.py index b0573dd..69c51cb 100644 --- a/backend/main.py +++ b/backend/main.py @@ -24,10 +24,13 @@ def supabase_check(): return JSONResponse(status_code=500, content={"status": "error", "error": str(e)}) @app.get("/me") -def me(user: dict = Depends(get_current_user)): +def me(current: dict = Depends(get_current_user)): return { - "id": user.get("sub"), - "email": user.get("email"), - "role": user.get("role"), - "tenant_id": user.get("tenant_id", "unknown") + "id": current["id"], + "email": current["email"], + "role": current["role"], + "tenant_id": current["tenant_id"], } + + +