from datetime import datetime, timedelta, timezone from typing import Optional import bcrypt import jwt from config import settings BCRYPT_ROUNDS = 12 def _truncate(pw: str) -> bytes: return pw.encode("utf-8")[:72] def hash_password(password: str) -> str: return bcrypt.hashpw(_truncate(password), bcrypt.gensalt(rounds=BCRYPT_ROUNDS)).decode() def verify_password(plain: str, hashed: str) -> bool: try: return bcrypt.checkpw(_truncate(plain), hashed.encode()) except (ValueError, TypeError): return False def create_access_token(subject: str | int, extra: Optional[dict] = None) -> str: now = datetime.now(timezone.utc) payload = { "sub": str(subject), "iat": int(now.timestamp()), "exp": int((now + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)).timestamp()), } if extra: payload.update(extra) return jwt.encode(payload, settings.SECRET_KEY, algorithm=settings.ALGORITHM) def decode_token(token: str) -> dict: return jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])