Сломалась авторизация пользователя в 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.

Для того, что бы пощупать эту технологию, обычно добавляют ссылку на ресурс в боте. И вуаля, сайт чудесным образом открывается по кнопке из ботика прямо в телеге.
Но дальше начинаются сложности (возможно не у всех, но у меня они точно были). Возникает потребность авторизоваться на этом ресурсе (сайте) через функционал телеграмм бота.

Дано:

5f8078346cefc523e7dde79c52c8fb41.png

  • Есть пользователь бота. Пользователем бота назовем юзера, который начал общение с ботом, посредством команды /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

    Добавили новое поле — signature

Поле добавили, функционал расширили, но о нас не подумали, и в итоге авторизация перестала работать, потому что хэш уже не тот что раньше. Теперь надо это поле добавлять в строку data_check_string вот так:

"auth_date=\nquery_id=\nsignature=\nuser="

Ждем обновления в офф доке.

© Habrahabr.ru