Верификация пользователя в Telegram MiniApps: различия между версиями
мНет описания правки |
мНет описания правки |
||
Строка 4: | Строка 4: | ||
Телеграм отдает строку с параметрами сессии в виде пар [[Ключ-Значение|ключ=значение]] разделенных знаком &. | Телеграм отдает строку с параметрами сессии в виде пар [[Ключ-Значение|ключ=значение]] разделенных знаком &. | ||
chat_instance=-987654321&chat_type=private&auth_date=1728578319&hash=da928ae18a63cb17d5db7457247ec7b7201ddb9e28636c140131542a218ed341&user={"id":12345687,"first_name":"Иван","last_name":"Иванов","username":"iiva","language_code":"ru","allows_write_to_pm":true} | |||
Сначала нужно разобрать полученную строку на массив пар ключ=значение, а после этого каждую пару также разделить отдельно на ключ и значение, сохранить их в новый массив для того, чтобы с ними можно было потом работать. | Сначала нужно разобрать полученную строку на массив пар ключ=значение, а после этого каждую пару также разделить отдельно на ключ и значение, сохранить их в новый массив для того, чтобы с ними можно было потом работать. | ||
Для разделения используется блок Split с параметром Separator=&, он отдаст нам массив строк в которых будут пары ключ=значение, после этого мы проходим в цикле по всем строкам полученного массива и еще раз через блок Split разделяем их с параметром Separator='='. В результате на каждой итерации мы будем получать массив из двух элементов - название параметра (ключ) и значение, которые нам нужно сохранить в отдельный массив. | Для разделения используется блок Split с параметром Separator=&, он отдаст нам массив строк в которых будут пары ключ=значение, после этого мы проходим в цикле по всем строкам полученного массива и еще раз через блок Split разделяем их с параметром Separator='='. В результате на каждой итерации мы будем получать массив из двух элементов - название параметра (ключ) и значение, которые нам нужно сохранить в отдельный массив. | ||
[[Файл:TG MiniApps initData verification split.png|центр|1000x1000пкс]] | [[Файл:TG MiniApps initData verification split.png|центр|1000x1000пкс]] | ||
'''Результат''' | |||
chat_instance: -987654321 | |||
chat_type: private | |||
auth_date: 1728578319 | |||
hash: da928ae18a63cb17d5db7457247ec7b7201ddb9e28636c140131542a218ed341 | |||
user: {"id":12345687,"first_name":"Иван","last_name":"Иванов","username":"iiva","language_code":"ru","allows_write_to_pm":true} | |||
В зависимости от параметра нам нужно выполнять с ними разные действия. | В зависимости от параметра нам нужно выполнять с ними разные действия. | ||
Версия от 03:55, 11 октября 2024
После запуска приложения в Telegram MiniApps становятся доступны данные пользователя, который запустил приложение, но для того чтобы быть уверенным, что они не подделаны, необходимо убедится, что они получены именно от Телеграмма.
Процесс валидации описан в документации на странице: https://core.telegram.org/bots/webapps#validating-data-received-via-the-mini-app
Телеграм отдает строку с параметрами сессии в виде пар ключ=значение разделенных знаком &.
chat_instance=-987654321&chat_type=private&auth_date=1728578319&hash=da928ae18a63cb17d5db7457247ec7b7201ddb9e28636c140131542a218ed341&user={"id":12345687,"first_name":"Иван","last_name":"Иванов","username":"iiva","language_code":"ru","allows_write_to_pm":true}
Сначала нужно разобрать полученную строку на массив пар ключ=значение, а после этого каждую пару также разделить отдельно на ключ и значение, сохранить их в новый массив для того, чтобы с ними можно было потом работать.
Для разделения используется блок Split с параметром Separator=&, он отдаст нам массив строк в которых будут пары ключ=значение, после этого мы проходим в цикле по всем строкам полученного массива и еще раз через блок Split разделяем их с параметром Separator='='. В результате на каждой итерации мы будем получать массив из двух элементов - название параметра (ключ) и значение, которые нам нужно сохранить в отдельный массив.
Результат chat_instance: -987654321 chat_type: private auth_date: 1728578319 hash: da928ae18a63cb17d5db7457247ec7b7201ddb9e28636c140131542a218ed341 user: {"id":12345687,"first_name":"Иван","last_name":"Иванов","username":"iiva","language_code":"ru","allows_write_to_pm":true}
В зависимости от параметра нам нужно выполнять с ними разные действия.
В параметре user хранится строка с JSON в котором содержатся данные о пользователе Телеграм. Строка приходит в формате безопасном для передачи в URL, поэтому для дальнейшей работы с ней её нужно раскодировать. После этого мы десериализуем её в модель данных, а также сохраняем в массив Ключ-Значение.
Остальные параметры мы также сохраняем в массив Ключ-Значение, а само название параметра (ключ) сохраняем в отдельный массив String array т.к. он нам в дальнейшем понадобится для формирования подписи.
Параметр hash содержит подпись переданного содержимого, и нужен нам в дальнейшем для того, чтобы сравнить с ним подпись созданную на нашей стороне. Мы его не сохраняем в общем массиве т.к. он не нужен для проверки подписи, нужно только его значение.
В результате работы цикла у нас получилось два массива:
1. Массив пар ключ-значение содержащий параметры пользователя.
2. Массив строк с ключами
Проверка подписи
Проверка подписи заключается в том, чтобы мы подписали полученные данные своим токеном от бота и сравнили результат с хешем полученным из параметров. Но подпись совпадет только в том случае, если подписываемая строка в точности совпадает. Для этого в соответствии с документацией требуется отсортировать все полученные параметры по алфавиту, соединить их в строки ключ=значение, а эти строки соединить через разделитель "перенос строки". Т.е. каждая пара должна быть на новой строке.
Для этого мы делаем сортировку по алфавиту массива строк с ключами через блок Sort Array.
После этого в цикле проходим по отсортированному массиву и берем из массива пар ключ-значение пару соответствующую этому ключу, объединяем ее в строку и добавляем в новый массив, в котором будут лежать строки в нужном порядке.
После окончания цикла, нам нужно собрать из полученного массива одну строку состоящую из значений разделенных переносом строки. Для этого используем блок Join Strings, но т.к. символ переноса строки является спецсимволом, он будет экранирован в дальнейшем т.е. вместо \n он будет заменен на \\n. Чтобы этого избежать мы передадим строку в формате текста в котором просто поставим пустой перенос. Чтобы уменьшить количество блоков можно значение указать сразу на входе блока To String, т.к. блок Join Strings в значение параметра Separator может получать только значение типа String.
На данном этапе мы сформировали правильную строку идентичную тому какой она была в момент подписания токеном бота на стороне телеграмма.
Проверка подписи осуществляется в два этапа.
Сначала нужно создать ключ, для этого в блок Crypto: HMAC Sign нужно передать в параметр Key строку "WebAppData", в параметр Value токен бота. Обе строки нужно предварительно сконвертировать в типа "байты" используя блок To Bytes. В параметре Hash Type необходимо выбрать тип - sha256.
На втором этапе мы выполняем тоже самое действие только в параметры блока Key нужно передать полученный ключ на предыдущем шаге из поля Signature, а в параметр Value строку, которую до этого собрали из массива, её также нужно сконвертировать в байты.
Полученную подпись конвертируем в шестнадцатеричную систему используя блок Bytes to HEX String. Теперь нам нужно сравнить полученную строку с хешем полученным в самом начале из параметров полученных от телеграмма. Если строки совпадают, значит полученные данные пользователя пришли от источника который знает токен вашего бота, т.е. телеграмма.
Важно! В случае если токен бота будет известен третьим лицам, они смогут сформировать подпись и передать данные любого пользователя в ваше приложение т.е. войти от имени любого пользователя. Для дополнительной безопасности, можно использовать одноразовые коды или отправлять подтверждения в бота пользователю т.к. злоумышленник не сможет получить к ним доступ.