from __future__ import annotations from datetime import datetime, timedelta, timezone from typing import Any from jose import jwt from passlib.context import CryptContext from backend.utils.env import Settings # `bcrypt` backend can be unreliable depending on the environment wheel build. # pbkdf2_sha256 avoids native bcrypt bindings and works consistently. pwd_context = CryptContext( schemes=["pbkdf2_sha256"], deprecated="auto", pbkdf2_sha256__rounds=290_000, ) def hash_password(password: str) -> str: return pwd_context.hash(password) def verify_password(password: str, password_hash: str) -> bool: return pwd_context.verify(password, password_hash) def create_access_token(settings: Settings, *, subject: str, extra: dict[str, Any] | None = None) -> str: now = datetime.now(timezone.utc) exp = now + timedelta(minutes=settings.jwt_exp_minutes) payload: dict[str, Any] = { "sub": subject, "iat": int(now.timestamp()), "exp": int(exp.timestamp()), } if extra: payload.update(extra) return jwt.encode(payload, settings.jwt_secret, algorithm=settings.jwt_algorithm) def decode_token(settings: Settings, token: str) -> dict[str, Any]: return jwt.decode(token, settings.jwt_secret, algorithms=[settings.jwt_algorithm])