Сломалась авторизация пользователя в Telegram Bot Mini App (Bot Api 8.0)
Спойлер: Причина написания статьи — сломалась авторизация в 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…
Для успешной авторизации пользователя нужно:
{
"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=
\nquery_id= \nuser= » Далее следуем алгоритму из документации:
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:
Добавили новое поле — signature
Поле добавили, функционал расширили, но о нас не подумали, и в итоге авторизация перестала работать, потому что хэш уже не тот что раньше. Теперь надо это поле добавлять в строку data_check_string вот так:
"auth_date=
Ждем обновления в офф доке.