Skip to content

Instantly share code, notes, and snippets.

@dantetemplar
Last active February 17, 2025 21:49
Show Gist options
  • Save dantetemplar/68e09e3674bf6ce72e23b89b2091d159 to your computer and use it in GitHub Desktop.
Save dantetemplar/68e09e3674bf6ce72e23b89b2091d159 to your computer and use it in GitHub Desktop.
Check Telegram WebAppData and LoginWidgetData in Python with FastAPI
"""
Check Telegram WebAppData and LoginWidgetData in Python with FastAPI.
Source: https://gist.github.com/dantetemplar/68e09e3674bf6ce72e23b89b2091d159
License: MIT License
Author: Ruslan Belkov
"""
import hashlib
import hmac
import json
import time
from fastapi import FastAPI, HTTPException, Request
from pydantic import BaseModel
from starlette.responses import HTMLResponse
BOT_TOKEN = "YOUR_BOT_TOKEN" # TODO: Replace with your bot token
BOT_USERNAME = "YOUR_BOT_USERNAME" # TODO: Replace with your bot username
FOR_WEBAPP = False # TODO: Set to True if you are using WebAppData, False if you are using LoginWidget
app = FastAPI()
class VerificationResult(BaseModel):
success: bool
telegram_user: dict | None = None
def telegram_check(params: dict) -> VerificationResult:
"""
Verify telegram data
https://core.telegram.org/widgets/login#checking-authorization
"""
if "hash" not in params or "auth_date" not in params:
return VerificationResult(success=False)
# use params = {k: v[0] for k, v in parse_qs(quoted_string).items()} if you have a string
received_hash = params["hash"]
auth_date = int(params["auth_date"])
to_hash = "\n".join([f"{k}={v}" for k, v in sorted(params.items()) if k != "hash"])
encoded_telegram_data = to_hash.encode("utf-8").decode("unicode-escape").encode("ISO-8859-1")
token = BOT_TOKEN
if FOR_WEBAPP: # Use hmac(key="WebAppData", msg=token, digestmode=sha256) as secret key
secret_key = hmac.new(b"WebAppData", token.encode(), hashlib.sha256).digest()
else: # Use just sha256(token) as secret key
secret_key = hashlib.sha256(token.encode()).digest()
evaluated_hash = hmac.new(secret_key, encoded_telegram_data, hashlib.sha256).hexdigest()
if time.time() - auth_date > 86400:
return VerificationResult(success=False) # Data is older than 24 hours
success = hmac.compare_digest(evaluated_hash, received_hash)
if success:
if FOR_WEBAPP:
user = json.loads(params["user"])
else:
user = {k: v for k, v in params.items() if k not in ["hash", "auth_date"]}
return VerificationResult(success=True, telegram_user=user)
else:
return VerificationResult(success=False) # Data is not from Telegram
@app.get("/telegram-widget.html", response_class=HTMLResponse)
async def telegram_widget(request: Request):
callback_url = request.url_for("telegram_callback")
content = f"""
<!DOCTYPE html>
<html>
<head>
<title>Telegram Widget</title>
</head>
<body>
<script async src="https://telegram.org/js/telegram-widget.js?22" data-telegram-login="{BOT_USERNAME}" data-size="medium" data-onauth="onTelegramAuth(user)" data-request-access="write"></script>
<script>
function onTelegramAuth(user) {{
console.log(user);
fetch("{callback_url}" + "?" + new URLSearchParams(user), {{"method": "POST"}})
.then(response => response.json())
.then(data => {{
if (data.success) {{
alert("Telegram data verified successfully");
}} else {{
alert("Telegram data verification failed");
}}
}})
.catch(error => {{
console.error("Error:", error);
}});
}}
</script>
</body>
</html>
"""
return HTMLResponse(content)
@app.post("/telegram-callback/")
async def telegram_callback(request: Request) -> VerificationResult:
result = telegram_check(request.query_params._dict)
if not result.success:
raise HTTPException(status_code=403, detail="Telegram data verification failed")
return result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment