Refactor configuration and enhance Telegram initData validation

- Improved formatting and readability in config.py and other files by adding line breaks.
- Introduced INIT_DATA_MAX_AGE_SECONDS to enforce replay protection for Telegram initData.
- Updated validate_init_data function to include max_age_seconds parameter for validation.
- Enhanced API to reject old initData based on the new max_age_seconds setting.
- Added tests for auth_date expiry and validation of initData in test_telegram_auth.py.
- Updated README with details on the new INIT_DATA_MAX_AGE_SECONDS configuration.
This commit is contained in:
2026-02-17 17:31:20 +03:00
parent d20a285f09
commit 1948618394
19 changed files with 181 additions and 25 deletions

View File

@@ -1,17 +1,26 @@
"""Validate Telegram Web App initData and extract user username."""
import hashlib
import hmac
import json
import time
from urllib.parse import unquote
# Telegram algorithm: https://core.telegram.org/bots/webapps#validating-data-received-via-the-mini-app
# Data-check string must use the same key=value pairs as received (sorted by key); we preserve raw values.
def validate_init_data(init_data: str, bot_token: str) -> str | None:
def validate_init_data(
init_data: str,
bot_token: str,
max_age_seconds: int | None = None,
) -> str | None:
"""
Validate initData signature and return the Telegram username (lowercase, no @).
Returns None if data is invalid, forged, or user has no username.
If max_age_seconds is set, initData must include auth_date and it must be no older
than max_age_seconds (replay protection). Example: 86400 = 24 hours.
"""
if not init_data or not bot_token:
return None
@@ -45,6 +54,17 @@ def validate_init_data(init_data: str, bot_token: str) -> str | None:
).hexdigest()
if not hmac.compare_digest(computed.lower(), hash_val.lower()):
return None
# Optional replay protection: reject initData older than max_age_seconds
if max_age_seconds is not None and max_age_seconds > 0:
auth_date_raw = params.get("auth_date")
if not auth_date_raw:
return None
try:
auth_date = int(float(auth_date_raw))
except (ValueError, TypeError):
return None
if time.time() - auth_date > max_age_seconds:
return None
# Parse user JSON (value may be URL-encoded in the raw string)
user_raw = params.get("user")
if not user_raw: