95 lines
2.7 KiB
Python
95 lines
2.7 KiB
Python
import hashlib
|
|
import hmac
|
|
import time
|
|
|
|
|
|
def verify_telegram_login(
|
|
payload: dict,
|
|
bot_token: str,
|
|
*,
|
|
max_age_seconds: int = 86400,
|
|
) -> bool:
|
|
"""
|
|
Проверяет подпись и свежесть данных от Telegram Login Widget.
|
|
"""
|
|
if not payload or not bot_token:
|
|
return False
|
|
received_hash = payload.get("hash")
|
|
if not received_hash:
|
|
return False
|
|
auth_date = payload.get("auth_date")
|
|
if auth_date is None:
|
|
return False
|
|
try:
|
|
if int(auth_date) < time.time() - max_age_seconds:
|
|
return False
|
|
except (TypeError, ValueError):
|
|
return False
|
|
|
|
check_parts = sorted((k, v) for k, v in payload.items() if k != "hash" and v is not None)
|
|
data_check_string = "\n".join(f"{k}={v}" for k, v in check_parts)
|
|
|
|
secret_key = hashlib.sha256(bot_token.encode()).digest()
|
|
computed = hmac.new(secret_key, data_check_string.encode(), hashlib.sha256).hexdigest()
|
|
|
|
return hmac.compare_digest(computed, received_hash)
|
|
|
|
|
|
def verify_webapp_init_data(
|
|
init_data: str,
|
|
bot_token: str,
|
|
*,
|
|
max_age_seconds: int = 86400,
|
|
) -> dict | None:
|
|
"""
|
|
Валидирует Telegram WebApp initData (HMAC-SHA256).
|
|
Возвращает dict с user_id или None если невалидно.
|
|
https://core.telegram.org/bots/webapps#validating-data-received-via-the-mini-app
|
|
"""
|
|
import json
|
|
from urllib.parse import parse_qs
|
|
|
|
if not init_data or not bot_token:
|
|
return None
|
|
|
|
parsed = parse_qs(init_data, keep_blank_values=True)
|
|
received_hash = parsed.get("hash", [""])[0]
|
|
if not received_hash:
|
|
return None
|
|
|
|
auth_date_str = parsed.get("auth_date", [""])[0]
|
|
try:
|
|
auth_date = int(auth_date_str)
|
|
if auth_date < time.time() - max_age_seconds:
|
|
return None
|
|
except (TypeError, ValueError):
|
|
return None
|
|
|
|
check_pairs = []
|
|
for key in sorted(parsed.keys()):
|
|
if key == "hash":
|
|
continue
|
|
check_pairs.append(f"{key}={parsed[key][0]}")
|
|
data_check_string = "\n".join(check_pairs)
|
|
|
|
secret_key = hmac.new(b"WebAppData", bot_token.encode(), hashlib.sha256).digest()
|
|
computed = hmac.new(secret_key, data_check_string.encode(), hashlib.sha256).hexdigest()
|
|
|
|
if not hmac.compare_digest(computed, received_hash):
|
|
return None
|
|
|
|
user_raw = parsed.get("user", [""])[0]
|
|
user_id = None
|
|
if user_raw:
|
|
try:
|
|
user_data = json.loads(user_raw)
|
|
user_id = user_data.get("id")
|
|
except (json.JSONDecodeError, TypeError):
|
|
pass
|
|
|
|
return {
|
|
"user_id": user_id,
|
|
"auth_date": auth_date,
|
|
"user_raw": user_raw,
|
|
}
|