ThreadHouse / app /auth_deps.py
paudelapil's picture
Project do-over
65fa0c5
Raw
History Blame Contribute Delete
2.52 kB
import os
from typing import Optional
import jwt
from fastapi import Header, HTTPException, status
JWT_SECRET = os.getenv("JWT_SECRET", "")
JWT_ALGO = "HS256"
def _decode(token: str) -> dict:
return jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGO])
def _strip_bearer(authorization: Optional[str]) -> Optional[str]:
if not authorization:
return None
parts = authorization.split(None, 1)
if len(parts) == 2 and parts[0].lower() == "bearer":
return parts[1].strip()
# Some frontends send the raw token (admin panel sets a `token` header).
return authorization.strip()
def get_current_user_id(
authorization: Optional[str] = Header(None),
token: Optional[str] = Header(None), # admin frontend's `token` header
) -> int:
raw = _strip_bearer(authorization) or (token.strip() if token else None)
if not raw:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Missing Authorization token",
)
try:
payload = _decode(raw)
except jwt.ExpiredSignatureError:
raise HTTPException(401, "Token expired")
except jwt.InvalidTokenError:
raise HTTPException(401, "Invalid token")
try:
return int(payload["sub"])
except (KeyError, ValueError, TypeError):
raise HTTPException(401, "Token has no user id")
def get_optional_user_id(
authorization: Optional[str] = Header(None),
token: Optional[str] = Header(None),
) -> Optional[int]:
"""Like get_current_user_id, but returns None if no/invalid token."""
raw = _strip_bearer(authorization) or (token.strip() if token else None)
if not raw:
return None
try:
payload = _decode(raw)
return int(payload["sub"])
except Exception:
return None
# Admin guard
import asyncpg as _asyncpg
from fastapi import Depends as _Depends
from app.db.asyncpg_pool import get_asyncpg_conn as _get_conn
async def get_current_admin(
user_id: int = _Depends(get_current_user_id),
conn: _asyncpg.Connection = _Depends(_get_conn),
) -> int:
"""
Validates the JWT, then checks the user's role in the DB.
Returns user_id on success; raises 403 if the user isn't an admin.
"""
role = await conn.fetchval(
"SELECT role FROM users WHERE id = $1", user_id
)
if role != "admin":
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Admin access required",
)
return user_id