Files
remnawave-bedolaga-telegram…/app/utils/telegram_webapp.py
c0mrade 4234769e92 revert: remove signature pop from HMAC validation
Telegram includes signature in the hash computation, so removing it
from the data-check-string breaks HMAC validation for all users.
2026-02-06 22:27:57 +03:00

89 lines
2.6 KiB
Python

"""Utilities for validating Telegram WebApp initialization data."""
from __future__ import annotations
import hashlib
import hmac
import json
import time
from typing import Any
from urllib.parse import parse_qsl
class TelegramWebAppAuthError(Exception):
"""Raised when Telegram WebApp init data fails validation."""
def parse_webapp_init_data(
init_data: str,
bot_token: str,
*,
max_age_seconds: int = 86400,
) -> dict[str, Any]:
"""Validate and parse Telegram WebApp init data.
Args:
init_data: Raw init data string provided by Telegram WebApp.
bot_token: Bot token used to verify the signature.
max_age_seconds: Maximum allowed age for the payload. Defaults to 24 hours.
Returns:
Parsed init data as a dictionary.
Raises:
TelegramWebAppAuthError: If validation fails.
"""
if not init_data:
raise TelegramWebAppAuthError('Missing init data')
if not bot_token:
raise TelegramWebAppAuthError('Bot token is not configured')
parsed_pairs = parse_qsl(init_data, strict_parsing=True, keep_blank_values=True)
data: dict[str, Any] = {key: value for key, value in parsed_pairs}
received_hash = data.pop('hash', None)
if not received_hash:
raise TelegramWebAppAuthError('Missing init data signature')
data_check_string = '\n'.join(f'{key}={value}' for key, value in sorted(data.items()))
secret_key = hmac.new(
key=b'WebAppData',
msg=bot_token.encode('utf-8'),
digestmod=hashlib.sha256,
).digest()
computed_hash = hmac.new(
key=secret_key,
msg=data_check_string.encode('utf-8'),
digestmod=hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(computed_hash, received_hash):
raise TelegramWebAppAuthError('Invalid init data signature')
auth_date_raw = data.get('auth_date')
if auth_date_raw is not None:
try:
auth_date = int(auth_date_raw)
except (TypeError, ValueError):
raise TelegramWebAppAuthError('Invalid auth_date value') from None
if max_age_seconds and auth_date:
current_ts = int(time.time())
if current_ts - auth_date > max_age_seconds:
raise TelegramWebAppAuthError('Init data is too old')
data['auth_date'] = auth_date
user_payload = data.get('user')
if user_payload is not None:
try:
data['user'] = json.loads(user_payload)
except json.JSONDecodeError as error:
raise TelegramWebAppAuthError('Invalid user payload') from error
return data