initData: проверка подлинности пользователя на бэкенде Проверишь подпись initData через HMAC-SHA256 и безопасно получишь user id на сервере.
Зачем валидировать initData
Telegram.WebApp.initData — строка параметров (как query string), которую Telegram подписывает. На клиенте её легко подделать. Источник истины — проверка HMAC на бэкенде с токеном бота: только после этого можно доверять user.id и выдавать сессию.
Без проверки любой может отправить на API { user: { id: 1 } } и выдать себя за другого.
Проверяем подпись на Node.js
Алгоритм из официальной документации :
Из initData достаёшь hash, остальные пары сортируешь и склеиваешь через \n.
secret_key = HMAC_SHA256(bot_token, key = "WebAppData").
Сравниваешь hex(HMAC_SHA256(data_check_string, secret_key)) с hash.
Дополнительно отбрасываешь устаревшие данные по auth_date.
typescript
Копировать// Node.js 20+, встроенный crypto (Bot API WebApps validating data)
import { createHmac, timingSafeEqual } from "node:crypto" ;
const MAX_AGE_SEC = 60 * 60 ; // 1 час — подстрой под продукт
export type TelegramWebAppUser = {
id : number ;
first_name : string ;
last_name ?: string ;
username ?: string ;
language_code ?: string ;
is_premium ?: boolean ;
};
export function validateInitData (
initData : string ,
botToken : string ,
maxAgeSec = MAX_AGE_SEC ,
) : { user : TelegramWebAppUser ; authDate : number } {
const params = new URLSearchParams (initData);
const hash = params. get ( "hash" );
if ( ! hash) {
throw new Error ( "initData: отсутствует hash" );
}
params. delete ( "hash" );
const dataCheckString = [ ... params. entries ()]
. sort (([ a ], [ b ]) => a. localeCompare (b))
. map (([ key , value ]) => `${ key }=${ value }` )
. join ( " \n " );
const secretKey = createHmac ( "sha256" , "WebAppData" )
. update (botToken)
. digest ();
const calculated = createHmac ( "sha256" , secretKey)
. update (dataCheckString)
. digest ( "hex" );
const calculatedBuf = Buffer. from (calculated, "hex" );
const hashBuf = Buffer. from (hash, "hex" );
if (
calculatedBuf. length !== hashBuf. length ||
! timingSafeEqual (calculatedBuf, hashBuf)
) {
throw new Error ( "initData: неверная подпись" );
}
const authDate = Number (params. get ( "auth_date" ));
if ( ! Number. isFinite (authDate)) {
throw new Error ( "initData: нет auth_date" );
}
const now = Math. floor (Date. now () / 1000 );
if (now - authDate > maxAgeSec) {
throw new Error ( "initData: данные устарели" );
}
const userRaw = params. get ( "user" );
if ( ! userRaw) {
throw new Error ( "initData: нет user" );
}
const user = JSON . parse (userRaw) as TelegramWebAppUser ;
if ( ! user?.id) {
throw new Error ( "initData: некорректный user" );
}
return { user, authDate };
}
// Пример Express-роута
import express from "express" ; // express ^4.21
const app = express ();
app. use (express. json ());
app. post ( "/api/auth" , ( req , res ) => {
try {
const { initData } = req.body as { initData ?: string };
if ( ! initData) {
res. status ( 400 ). json ({ error: "initData required" });
return ;
}
const { user } = validateInitData (initData, process.env. BOT_TOKEN ! );
// Здесь создаёшь JWT / session cookie, привязанную к user.id
res. json ({ ok: true , telegramId: user.id, name: user.first_name });
} catch {
res. status ( 401 ). json ({ error: "unauthorized" });
}
});
На клиенте после ready():
typescript
Копироватьconst initData = window.Telegram.WebApp.initData;
await fetch ( "https://api.example.com/api/auth" , {
method: "POST" ,
headers: { "Content-Type" : "application/json" },
body: JSON . stringify ({ initData }),
});
Как это работает
hash — HMAC от всех полей кроме самого hash.
secret_key — HMAC от токена бота с ключом "WebAppData" (не путать с Login Widget, там другой алгоритм).
auth_date — unix-время выдачи; без TTL replay возможен.
user — JSON-строка внутри query; парсишь только после успешной проверки hash.
Частые ошибки
Валидируешь только на клиенте или читаешь initDataUnsafe → для продакшна недостаточно. Сессию выдаёт только бэкенд после HMAC.
Путаешь алгоритм с Telegram Login Widget → у Login Widget другой secret (SHA256(bot_token)). Для Mini App — HMAC с ключом "WebAppData".
Не проверяешь auth_date → украденный старый initData можно переиспользовать. Ставь разумный TTL.
Что дальше
НазадTelegram WebApp API: инициализация приложения Далее MainButton и BackButton: нативные элементы Telegram Материалы gramdocs.tech носят образовательный характер и не являются финансовой, юридической или инвестиционной рекомендацией. Работа с блокчейном TON и токеном Gram связана с рисками потери средств. Правовая информация
На этой странице