JWT SImple implimentation

This commit is contained in:
Mike Kell 2025-07-24 15:10:10 -04:00
parent e20f0c713f
commit b1527defc2
2 changed files with 61 additions and 35 deletions

View File

@ -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"),
}

View File

@ -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"],
}