from datetime import datetime, timedelta, timezone from typing import Any, Dict, Optional from jose import JWTError, jwt from passlib.context import CryptContext from .config import settings _pwd_context = CryptContext(schemes=["pbkdf2_sha256"], deprecated="auto") _ALGORITHM = "HS256" def hash_password(password: str) -> str: return _pwd_context.hash(password) def verify_password(plain_password: str, hashed_password: str) -> bool: return _pwd_context.verify(plain_password, hashed_password) def create_access_token(subject: str, role: str, expires_minutes: Optional[int] = None) -> str: minutes = expires_minutes or settings.jwt_exp_minutes expires = datetime.now(timezone.utc) + timedelta(minutes=minutes) payload: Dict[str, Any] = {"sub": subject, "role": role, "exp": expires} return jwt.encode(payload, settings.jwt_secret, algorithm=_ALGORITHM) def decode_token(token: str) -> Dict[str, Any]: return jwt.decode(token, settings.jwt_secret, algorithms=[_ALGORITHM]) class TokenError(Exception): pass def safe_decode_token(token: str) -> Dict[str, Any]: try: return decode_token(token) except JWTError as exc: raise TokenError("Invalid token") from exc