iTagPDF / backend /dependencies.py
peyajm
Sync latest backend and pipeline code
fb7b1a4
Raw
History Blame
1.84 kB
"""
FastAPI dependencies for authentication.
"""
from datetime import datetime, timedelta, timezone
from typing import Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from fastapi.concurrency import run_in_threadpool
from jose import JWTError, jwt
import database
from config import JWT_SECRET
_bearer = HTTPBearer(auto_error=False)
_ALGORITHM = "HS256"
_TOKEN_EXPIRE_DAYS = 30
def create_access_token(data: dict) -> str:
payload = data.copy()
payload["exp"] = datetime.now(timezone.utc) + timedelta(days=_TOKEN_EXPIRE_DAYS)
return jwt.encode(payload, JWT_SECRET, algorithm=_ALGORITHM)
async def _decode_token(credentials: Optional[HTTPAuthorizationCredentials]) -> Optional[dict]:
if credentials is None:
return None
try:
payload = jwt.decode(credentials.credentials, JWT_SECRET, algorithms=[_ALGORITHM])
user_id: str = payload.get("sub")
if not user_id:
return None
return await run_in_threadpool(database.get_user_by_id, user_id)
except JWTError:
return None
async def get_current_user(
credentials: Optional[HTTPAuthorizationCredentials] = Depends(_bearer),
) -> dict:
"""Require a valid JWT; raise 401 otherwise."""
user = await _decode_token(credentials)
if user is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not authenticated.",
headers={"WWW-Authenticate": "Bearer"},
)
return user
async def get_optional_user(
credentials: Optional[HTTPAuthorizationCredentials] = Depends(_bearer),
) -> Optional[dict]:
"""Return the authenticated user or None (for routes that work with or without auth)."""
return await _decode_token(credentials)