mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-01-20 11:50:27 +00:00
140 lines
4.3 KiB
Python
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)
|