import hashlib import hmac import secrets from dataclasses import dataclass PBKDF2_ITERATIONS = 120_000 @dataclass(frozen=True) class AuthenticatedUser: id: str username: str nickname: str def hash_password(password: str) -> str: salt = secrets.token_hex(16) digest = hashlib.pbkdf2_hmac( "sha256", password.encode("utf-8"), salt.encode("ascii"), PBKDF2_ITERATIONS, ).hex() return f"pbkdf2_sha256${PBKDF2_ITERATIONS}${salt}${digest}" def verify_password(password: str, stored: str) -> bool: try: algorithm, iterations, salt, digest = stored.split("$", 3) if algorithm != "pbkdf2_sha256": return False calculated = hashlib.pbkdf2_hmac( "sha256", password.encode("utf-8"), salt.encode("ascii"), int(iterations), ).hex() return hmac.compare_digest(calculated, digest) except (TypeError, ValueError): return False def new_token() -> str: return secrets.token_urlsafe(48) def hash_token(token: str) -> str: return hashlib.sha256(token.encode("utf-8")).hexdigest()