from __future__ import annotations import hashlib import hmac import time from typing import Any class TelegramAuthError(ValueError): pass def verify_telegram_login( payload: dict[str, Any], bot_token: str, *, max_age_seconds: int = 86400, ) -> dict[str, Any]: """Verify Telegram Login Widget data per Telegram docs.""" received_hash = payload.get("hash") if not received_hash or not isinstance(received_hash, str): raise TelegramAuthError("Missing hash") check_pairs: list[str] = [] for key in sorted(payload.keys()): if key == "hash": continue value = payload[key] if value is None: continue check_pairs.append(f"{key}={value}") data_check_string = "\n".join(check_pairs) secret_key = hashlib.sha256(bot_token.encode("utf-8")).digest() calculated = hmac.new( secret_key, data_check_string.encode("utf-8"), hashlib.sha256, ).hexdigest() if not hmac.compare_digest(calculated, received_hash): raise TelegramAuthError("Invalid Telegram login signature") auth_date_raw = payload.get("auth_date") try: auth_date = int(auth_date_raw) except (TypeError, ValueError) as exc: raise TelegramAuthError("Invalid auth_date") from exc if max_age_seconds > 0 and time.time() - auth_date > max_age_seconds: raise TelegramAuthError("Telegram login data expired") try: user_id = int(payload["id"]) except (KeyError, TypeError, ValueError) as exc: raise TelegramAuthError("Missing Telegram user id") from exc return { "id": user_id, "first_name": str(payload.get("first_name") or ""), "last_name": str(payload.get("last_name") or "") or None, "username": str(payload.get("username") or "") or None, "photo_url": str(payload.get("photo_url") or "") or None, "auth_date": auth_date, }