Files
remnawave-bedolaga-telegram…/app/cabinet/auth/telegram_auth.py
PEDZEO c41979bda6 fix
2026-01-02 00:48:08 +03:00

140 lines
4.3 KiB
Python

"""Telegram authentication validation for cabinet."""
import hashlib
import hmac
import json
from datetime import datetime
from typing import Dict, Any, Optional
from urllib.parse import parse_qsl, unquote
from app.config import settings
def validate_telegram_login_widget(data: Dict[str, Any], max_age_seconds: int = 86400) -> bool:
"""
Validate Telegram Login Widget data.
https://core.telegram.org/widgets/login#checking-authorization
Args:
data: Dictionary with Telegram login data (id, first_name, auth_date, hash, etc.)
max_age_seconds: Maximum allowed age of auth_date (default 24 hours)
Returns:
True if data is valid, False otherwise
"""
auth_data = data.copy()
check_hash = auth_data.pop("hash", None)
if not check_hash:
return False
# Check auth_date is not too old
auth_date = auth_data.get("auth_date")
if auth_date:
try:
# Use UTC timestamp to avoid timezone issues
auth_time = datetime.utcfromtimestamp(int(auth_date))
age = (datetime.utcnow() - auth_time).total_seconds()
if age > max_age_seconds:
return False
except (ValueError, TypeError, OSError):
return False
# Build data-check-string (sorted key=value pairs, newline-separated)
data_check_arr = [f"{k}={v}" for k, v in sorted(auth_data.items()) if v is not None]
data_check_string = "\n".join(data_check_arr)
# Create secret key from bot token using SHA256
bot_token = settings.BOT_TOKEN
secret_key = hashlib.sha256(bot_token.encode()).digest()
# Calculate expected hash
calculated_hash = hmac.new(
secret_key,
data_check_string.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(calculated_hash, check_hash)
def validate_telegram_init_data(init_data: str, max_age_seconds: int = 86400) -> Optional[Dict[str, Any]]:
"""
Validate Telegram WebApp initData.
https://core.telegram.org/bots/webapps#validating-data-received-via-the-mini-app
Args:
init_data: Raw initData string from Telegram WebApp
max_age_seconds: Maximum allowed age of auth_date (default 24 hours)
Returns:
Parsed user data dict if valid, None otherwise
"""
try:
# Parse the init_data string
parsed = dict(parse_qsl(init_data, keep_blank_values=True))
received_hash = parsed.pop("hash", None)
if not received_hash:
return None
# Check auth_date is not too old
auth_date = parsed.get("auth_date")
if auth_date:
try:
# Use UTC timestamp to avoid timezone issues
auth_time = datetime.utcfromtimestamp(int(auth_date))
age = (datetime.utcnow() - auth_time).total_seconds()
if age > max_age_seconds:
return None
except (ValueError, TypeError, OSError):
return None
# Build data-check-string
data_check_arr = [f"{k}={v}" for k, v in sorted(parsed.items())]
data_check_string = "\n".join(data_check_arr)
# Create secret key: HMAC_SHA256(bot_token, "WebAppData")
bot_token = settings.BOT_TOKEN
secret_key = hmac.new(
b"WebAppData",
bot_token.encode(),
hashlib.sha256
).digest()
# Calculate expected hash
calculated_hash = hmac.new(
secret_key,
data_check_string.encode(),
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(calculated_hash, received_hash):
return None
# Parse user data from the validated data
user_data_str = parsed.get("user")
if user_data_str:
user_data = json.loads(unquote(user_data_str))
return user_data
return parsed
except (ValueError, TypeError, json.JSONDecodeError):
return None
def extract_telegram_user_from_init_data(init_data: str) -> Optional[Dict[str, Any]]:
"""
Extract and validate user info from Telegram WebApp initData.
Args:
init_data: Raw initData string from Telegram WebApp
Returns:
User data dict with id, first_name, last_name, username, etc. or None if invalid
"""
return validate_telegram_init_data(init_data)