from __future__ import annotations from datetime import datetime, timedelta, timezone from typing import Any import bcrypt from jose import jwt from app.core.config import settings def hash_password(password: str) -> str: # Use bcrypt directly to avoid passlib<->bcrypt version incompatibilities (bcrypt>=4 removed __about__). salt = bcrypt.gensalt(rounds=12) return bcrypt.hashpw(password.encode("utf-8"), salt).decode("utf-8") def verify_password(password: str, password_hash: str) -> bool: try: return bcrypt.checkpw(password.encode("utf-8"), password_hash.encode("utf-8")) except Exception: return False def create_token(*, subject: str, token_type: str, expires_delta: timedelta, extra: dict[str, Any] | None = None) -> str: now = datetime.now(timezone.utc) payload: dict[str, Any] = { "sub": subject, "typ": token_type, "iat": int(now.timestamp()), "exp": int((now + expires_delta).timestamp()), } if extra: payload.update(extra) return jwt.encode(payload, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM) def create_access_token(user_id: str) -> str: return create_token( subject=user_id, token_type="access", expires_delta=timedelta(minutes=settings.ACCESS_TOKEN_EXPIRES_MINUTES), ) def create_refresh_token(user_id: str) -> str: return create_token( subject=user_id, token_type="refresh", expires_delta=timedelta(days=settings.REFRESH_TOKEN_EXPIRES_DAYS), )