import hashlib import secrets from datetime import UTC, datetime, timedelta from typing import Any from cryptography.fernet import Fernet, InvalidToken from jose import JWTError, jwt from passlib.context import CryptContext from app.core.config import get_settings pwd_context = CryptContext(schemes=["argon2"], deprecated="auto") ALGORITHM = "HS256" def hash_password(password: str) -> str: return pwd_context.hash(password) def verify_password(password: str, password_hash: str | None) -> bool: return bool(password_hash) and pwd_context.verify(password, password_hash) def create_session_token(user_id: str, minutes: int = 60 * 24 * 14) -> str: expires_at = datetime.now(UTC) + timedelta(minutes=minutes) payload: dict[str, Any] = {"sub": user_id, "exp": expires_at} return jwt.encode(payload, get_settings().jwt_secret_key, algorithm=ALGORITHM) def decode_session_token(token: str) -> str | None: try: payload = jwt.decode(token, get_settings().jwt_secret_key, algorithms=[ALGORITHM]) return str(payload.get("sub")) except JWTError: return None def new_token() -> str: return secrets.token_urlsafe(32) def hash_token(token: str) -> str: return hashlib.sha256(token.encode("utf-8")).hexdigest() def encrypt_secret(value: str | None) -> str | None: if not value: return None return Fernet(get_settings().settings_secret_key.encode("utf-8")).encrypt( value.encode("utf-8") ).decode("utf-8") def decrypt_secret(value: str | None) -> str | None: if not value: return None try: return Fernet(get_settings().settings_secret_key.encode("utf-8")).decrypt( value.encode("utf-8") ).decode("utf-8") except InvalidToken: return None def make_csrf_token() -> str: return secrets.token_urlsafe(24)