auth: fallback на initDataUnsafe для Telegram Desktop side-panel

В Telegram Desktop при открытии MiniApp в side-panel (boxed mode)
WebApp.initData приходит пустой. Backend не может проверить подпись.

Временный fallback: если initData пустой, доверяем initDataUnsafe.user
для определения роли. Action-endpoints (grant_role, measurement,
podbor) продолжают требовать подписанный initData.

Cache bust v=20260513i.
This commit is contained in:
wasrusgen 2026-05-12 21:35:51 +03:00
parent ee619bb57d
commit cb6398622b
3 changed files with 30 additions and 11 deletions

View File

@ -5,6 +5,7 @@ import json
import logging
import os
import re
import time
import uuid
from datetime import datetime, timezone
from pathlib import Path
@ -427,8 +428,23 @@ def _handle_me(body: dict[str, Any]) -> dict[str, Any]:
init_data = body.get("initData") or ""
auth = verify_init_data(init_data, cfg.bot_token)
print(f"[ME] auth result: ok={bool(auth)} user_present={bool(auth and auth.get('user'))}", flush=True, file=sys.stderr)
# Fallback для Telegram Desktop side-panel — initData может приходить пустым.
# Доверяем initDataUnsafe.user (НЕпроверенным данным) — только для UI-режима.
# Все endpoint-ы, выполняющие действия, продолжают требовать подписанный initData.
if not auth or not auth.get("user"):
return {"error": "invalid_init_data"}
unsafe = body.get("initDataUnsafe") or {}
unsafe_user = unsafe.get("user") if isinstance(unsafe, dict) else None
if unsafe_user and unsafe_user.get("id"):
print(f"[ME] FALLBACK: using initDataUnsafe.user id={unsafe_user.get('id')}", flush=True, file=sys.stderr)
auth = {
"user": unsafe_user,
"auth_date": int(time.time()),
"start_param": unsafe.get("start_param"),
"_unsafe": True,
}
else:
return {"error": "invalid_init_data"}
tg_user = auth["user"]
tg_id = tg_user["id"]

View File

@ -63,6 +63,9 @@ async function fetchMe() {
method: "POST",
body: JSON.stringify({
initData: tg?.initData || "",
// Fallback для Telegram Desktop side-panel где initData может приходить пустым.
// Backend проверит подпись initData первым; если её нет — упадёт сюда. UNSAFE!
initDataUnsafe: tg?.initDataUnsafe || null,
startParam: tg?.initDataUnsafe?.start_param || null,
role: explicitRole,
}),

View File

@ -12,8 +12,8 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Geist:wght@400;500;600&family=Newsreader:ital,wght@0,400..600;1,400..600&family=Instrument+Serif:ital@0;1&family=JetBrains+Mono:wght@400;500&display=swap">
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<link rel="stylesheet" href="assets/styles.css?v=20260513h">
<link rel="stylesheet" href="assets/podbor.css?v=20260513h">
<link rel="stylesheet" href="assets/styles.css?v=20260513i">
<link rel="stylesheet" href="assets/podbor.css?v=20260513i">
</head>
<body>
<!-- Splash — за пределами #app, render-функции его не смывают -->
@ -34,13 +34,13 @@
</div>
</div>
<main id="app"></main>
<script src="assets/icons.js?v=20260513h"></script>
<script src="assets/podbor.config.js?v=20260513h"></script>
<script src="assets/podbor.picts.js?v=20260513h"></script>
<script src="assets/podbor.js?v=20260513h"></script>
<script src="assets/clients.js?v=20260513h"></script>
<script src="assets/measurements.js?v=20260513h"></script>
<script src="assets/request.js?v=20260513h"></script>
<script src="assets/app.js?v=20260513h"></script>
<script src="assets/icons.js?v=20260513i"></script>
<script src="assets/podbor.config.js?v=20260513i"></script>
<script src="assets/podbor.picts.js?v=20260513i"></script>
<script src="assets/podbor.js?v=20260513i"></script>
<script src="assets/clients.js?v=20260513i"></script>
<script src="assets/measurements.js?v=20260513i"></script>
<script src="assets/request.js?v=20260513i"></script>
<script src="assets/app.js?v=20260513i"></script>
</body>
</html>