mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-02-22 12:21:26 +00:00
Telegram includes signature in the hash computation, so removing it from the data-check-string breaks HMAC validation for all users.
89 lines
2.6 KiB
Python
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
|