import base64 import hashlib import hmac import json from datetime import datetime, timedelta, timezone from backend.app.core.config import get_settings settings = get_settings() def hash_password(password: str) -> str: secret = settings.secret_key.encode("utf-8") return hashlib.sha256(secret + password.encode("utf-8")).hexdigest() def verify_password(password: str, password_hash: str) -> bool: return hmac.compare_digest(hash_password(password), password_hash or "") def create_access_token(payload: dict) -> str: data = dict(payload) expire_at = datetime.now(timezone.utc) + timedelta(minutes=settings.jwt_expire_minutes) data["exp"] = int(expire_at.timestamp()) body = json.dumps(data, ensure_ascii=False, separators=(",", ":")).encode("utf-8") body_b64 = base64.urlsafe_b64encode(body).decode("utf-8").rstrip("=") signature = hmac.new(settings.secret_key.encode("utf-8"), body_b64.encode("utf-8"), hashlib.sha256).hexdigest() return f"{body_b64}.{signature}" def decode_access_token(token: str) -> dict | None: try: body_b64, signature = token.split(".", 1) expected = hmac.new(settings.secret_key.encode("utf-8"), body_b64.encode("utf-8"), hashlib.sha256).hexdigest() if not hmac.compare_digest(signature, expected): return None padded = body_b64 + "=" * (-len(body_b64) % 4) payload = json.loads(base64.urlsafe_b64decode(padded.encode("utf-8")).decode("utf-8")) exp = int(payload.get("exp", 0)) if exp <= int(datetime.now(timezone.utc).timestamp()): return None return payload except Exception: return None