feat(proj): init
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
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,
|
||||
}
|
||||
Reference in New Issue
Block a user