from datetime import datetime, timedelta, timezone from passlib.context import CryptContext from passlib.exc import UnknownHashError from jose import jwt, JWTError from typing import Any from .config import get_settings import bcrypt # Monkey patch bcrypt for passlib compatibility if not hasattr(bcrypt, "__about__"): try: from bcrypt import __version__ class About: __version__ = __version__ bcrypt.__about__ = About() except ImportError: pass pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") settings = get_settings() def hash_password(password: str) -> str: return pwd_context.hash(password) def verify_password(plain_password: str, hashed_password: str) -> bool: try: return pwd_context.verify(plain_password, hashed_password) except UnknownHashError: return False def create_token(data: dict[str, Any], expires_delta: timedelta) -> str: to_encode = data.copy() expire = datetime.now(timezone.utc) + expires_delta to_encode.update({"exp": expire}) return jwt.encode(to_encode, settings.jwt_secret_key, algorithm=settings.jwt_algorithm) def decode_token(token: str) -> dict[str, Any]: try: payload = jwt.decode(token, settings.jwt_secret_key, algorithms=[settings.jwt_algorithm]) return payload except JWTError as exc: raise ValueError("Invalid token") from exc