Спойлер: Причина написания статьи - сломалась авторизация в Telegram боте Mini App после обновления Bot API 8.0, решение смотри внизу.
Всех приветствую! Относительно не так давно решил написать телеграмм бота под один небольшой проект, и под эту задачу решил изучить функционал Mini App и встроить работу с ним в своего бота (офф дока - https://core.telegram.org/bots/webapps).
Вообще, хочу пролить свет на понятие телеграмм бота не для разработчиков. Это отображение вашей программы в уже готовом приложении. Делаю на этом акцент, так как часто сталкиваюсь с непониманием трудозатрат только потому, что конечный результат называется "ботиком". Неискушенный заказчик считает, что задача от этого сильно упрощается, ведь это всего лишь ботик. Например, Вася с Петей на разных коленках сделают его за 5 минут в перерыве между парами за булочку с повидлом, а ты хочешь еще и денег? Для тех, кто больше занимается бэкендом, телеграмм бот без Mini App - отличное решение. Можно написать довольно сложный проект, интерфейс которого не надо придумывать, а только выбрать из набора допустимых возможностей - то что лучше подходит под вашу задачу. Но, к сожалению, такой вариант не всегда подходит, и в какой-то момент надо кастомизировать ваше приложение - вот тут и выходит на сцену Mini App.
Для того, что бы пощупать эту технологию, обычно добавляют ссылку на ресурс в боте. И вуаля, сайт чудесным образом открывается по кнопке из ботика прямо в телеге.
Но дальше начинаются сложности (возможно не у всех, но у меня они точно были). Возникает потребность авторизоваться на этом ресурсе (сайте) через функционал телеграмм бота.
Дано:
-
Есть пользователь бота. Пользователем бота назовем юзера, который начал общение с ботом, посредством команды /start.
-
Есть некий сервис (бэкенд + фронтенд) помимо бота, который мы и хотим интегрировать в ботика.
Задача:
-
Авторизовать пользователя в вашей система из вашего Телеграм бота по нажатию на кнопку открытия Mini APP. Например, зачем это надо: в вашей системе есть две роли - администратор и пользователь. Если авторизуется администратор, то он увидит некий "секретный" график, доступный только этой роли. Если авторизуется пользователь, то он график уже не увидит, а увидит, например, информацию о погоде.
То есть, мы хотим сделать "бесшовную" авторизацию в нашем сервисе из бота по одному нажатию на кнопку. Это будет эквивалентно тому, если бы вы зашли на ваш сайт и авторизовались, введя логин и пароль. Но для этого логин/пароль надо вводить и помнить, а наша задача этого избежать.
Если обратиться к офф доке (https://core.telegram.org/bots/webapps#validating-data-received-via-the-mini-app), то из нее мы должны почерпнуть базу:
при нажатии на кнопень, мы передаем данные как query params на нашу страничку и получаем их в специальном объекте на фронте - Telegram.WebApp.initData.
Для того, что бы все работало, к нашему фронту надо подключить файлик https://telegram.org/js/telegram-web-app.js, который как раз и будет заполнять значениями данный объект.
Пример:
Ваш сайт работает на домене https://my-best-examples.ru.
Как только вы нажмете на кнопку для открытия вашего Mini App-a (этого же сайта), будет отправлен такой запрос https://my-best-examples.ru/#tgWebAppData=query_id%3DAA...
Для успешной авторизации пользователя нужно:
-
Получить данные этой строки (в нашем случае можно их взять из Telegram.WebApp.initData) - это будет некий объект с набором таких данных:
{
"query_id": "AH8SAAAAAJaofxLgBX3V",
"user": {"id":123123,"first_name":"Example" ...},
"auth_date": 1826400455,
"hash": "44444ee44b55e126d14b106555555555555c77777777c8f12fb04a9d5",
"tgWebAppVersion": 7.8,
"tgWebAppThemeParams": {"accent_text_color":"#168acd","bg_color":"#ffffff" ...}
...
}
Ваша задача:
-
отсортировать по алфавиту параметры auth_date, query_id, user и записать их в одну строку вот так: data_check_string = "auth_date=<auth_date>nquery_id=<query_id>nuser=<user>"
-
Далее следуем алгоритму из документации:
data_check_string = <наша подготовленная строка>
secret_key = HMAC_SHA256(<токен вашего бота>, "WebAppData")
if (hex(HMAC_SHA256(data_check_string, secret_key)) == hash) {
// data is from Telegram
}"WebAppData" - это некое контрольное слово в виде строки (я сначала не понял, что тут имелось ввиду, поэтому детализирую):
hash - это строковое значение переменной из данных, которые прилетели в форму (заметьте, что в data_check_string хэша не должно быть). То есть это хэш данных, которые были отправлены к вам на вашу страничку, но не всех, а только "белого списка" параметров. Если все сделать правильно, то до версии Bot Api 7.8 так и работало, но есть одно НО!
-
Великое и ужасное НО:
С обновлением версии Bot Api 8.0 - 17 ноября 2024 такая авторизация сломалось. Мои исследования проблемы подсказок в этой офф доке на нашли, но я заметил в описании структуры принимаемых данных новое поле и новый тип авторизации для сторонних систем. А именно поле "siganture" в объекте WebAppInitData:
Поле добавили, функционал расширили, но о нас не подумали, и в итоге авторизация перестала работать, потому что хэш уже не тот что раньше. Теперь надо это поле добавлять в строку data_check_string вот так:
"auth_date=<auth_date>nquery_id=<query_id>nsignature=<signature>nuser=<user>"
Ждем обновления в офф доке.
Автор: JavaKrasava