import os from datetime import datetime, timedelta, timezone from typing import Optional from jose import jwt, JWTError from fastapi import Depends, HTTPException, status from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer JWT_SECRET = os.getenv("JWT_SECRET", "dev-secret-change-me") JWT_ALG = "HS256" JWT_TTL_MIN = int(os.getenv("JWT_TTL_MIN", "60")) def create_access_token(sub: str, extra: Optional[dict] = None) -> str: now = datetime.now(tz=timezone.utc) payload = { "sub": sub, "iat": int(now.timestamp()), "exp": int((now + timedelta(minutes=JWT_TTL_MIN)).timestamp()), } if extra: payload.update(extra) return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALG) bearer_scheme = HTTPBearer(auto_error=False) def get_current_subject(creds: HTTPAuthorizationCredentials = Depends(bearer_scheme)) -> str: if not creds or creds.scheme.lower() != "bearer": raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated") try: payload = jwt.decode(creds.credentials, JWT_SECRET, algorithms=[JWT_ALG]) return str(payload.get("sub")) except JWTError: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")