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 fastapi import Request, HTTPException, status, Depends
from jose import jwt from jose import jwt, JWTError
from jose.exceptions import JWTError import os
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from typing import Dict
SUPABASE_PROJECT_ID = "lcoretjgpauozmuoedus" # <-- Replace with your project ref JWT_SECRET = os.getenv("SUPABASE_JWT_SECRET")
SUPABASE_JWKS_URL = f"https://{SUPABASE_PROJECT_ID}.supabase.co/auth/v1/keys" ALGORITHM = "HS256"
# Fetch JWKs from Supabase
jwks = requests.get(SUPABASE_JWKS_URL).json()
# Define FastAPI's bearer auth scheme def get_token_from_header(request: Request) -> str:
auth_scheme = HTTPBearer() 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: try:
header = jwt.get_unverified_header(token) payload = jwt.decode(
kid = header["kid"] 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 return payload
except JWTError as e: 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( def get_current_user(request: Request):
credentials: HTTPAuthorizationCredentials = Depends(auth_scheme) token = get_token_from_header(request)
) -> Dict: payload = verify_jwt_token(token)
token = credentials.credentials
return 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)}) return JSONResponse(status_code=500, content={"status": "error", "error": str(e)})
@app.get("/me") @app.get("/me")
def me(user: dict = Depends(get_current_user)): def me(current: dict = Depends(get_current_user)):
return { return {
"id": user.get("sub"), "id": current["id"],
"email": user.get("email"), "email": current["email"],
"role": user.get("role"), "role": current["role"],
"tenant_id": user.get("tenant_id", "unknown") "tenant_id": current["tenant_id"],
} }