Авторизация пользователей через Telegram

8 min read

Недавно Telegram добавил поддержку виджета для авторизации пользователей на сайте. Мы решили поэкспериментировать с ним и составить простую инструкцию, как настроить такую авторизацию самостоятельно.

В качестве примера будем использовать код на PHP, однако, данные шаги актуальны и для других языков программирования.

Настройка бота

Для использования виджета вам понадобится Telegram-бот.

Перейдите в чат с системным пользователем @botfather. Если у вас ещё нет ни одного бота, создайте его командой /newbot. Посмотреть список своих пользователей вы можете с помощью команды /mybots.

Доступные опции после выбора бота из списка

Скопируйте токен бота, через которого вы хотите производить авторизацию пользователей.

Here is the token for bot codex_cloud @codex_cloud_bot: 558<...>728:AWBEwgUg<...>HBKuiINt

Название и аватарка выбранного вами бота будут показаны пользователю во всплывающем окне. А вы получите возможность отправлять пользователю личные сообщения через этого бота.

При первой авторизации пользователь увидит информацию о боте

Для каждого бота нужно привязать конкретный адрес сайта, на котором пользователи могут авторизоваться. В диалоге с @botfather введите команду /setdomain и напишите адрес сайта в виде http://ifmo.su.

Настройка виджета

На сайте можно получить код виджета и выбрать его внешний вид. К сожалению, возможностей для его произвольного конфигурирования на данный момент нет т.к. виджет встраивается на сайт посредством iframe.

Кастомизация виджета и получение кода для вставки на сайт

Встраивание на сайт

Создайте файл index.php со следующим содержанием

<?php # Поместите сюда название вашего бота, токен и адрес, на который Telegram перенаправит пользователя после авторизации $BOT_USERNAME = 'codex_cloud_bot'; $BOT_TOKEN = '558<...>728:AWBEwgUg<...>HBKuiINt'; $REDIRECT_URI = 'http://ifmo.su/auth/callback'; ?> <html> <body> <!-- Код виджета в том месте, где хотим видеть кнопку --> <script src="https://telegram.org/js/telegram-widget.js?2" data-telegram-login="<?= $BOT_USERNAME ?>" data-size="medium" data-auth-url="<?= $REDIRECT_URI ?>" data-request-access="write"></script> </body> </html>

После того, как пользователь нажмёт на кнопку, Telegram готов отправить вам данные любым из двух способов:

На данный момент поддерживаются следующие данные о пользователе:

Получение данных через JavaScript callback

Выберите в конструкторе виджета опцию Authorization Type: Callback. Сгенерированный в результате код виджета содержит JavaScript функцию, которая будет вызвана после успешной авторизации.

function onTelegramAuth(user) { alert('Logged in as ' + user.first_name + ' ' + user.last_name + ' (' + user.id + (user.username ? ', @' + user.username : '') + ')'); }

Эту функцию нужно передать в аттрибуте data-onauth тега

<script async src="https://telegram.org/js/telegram-widget.js?2" data-telegram-login="@codex_bot" data-size="medium" data-onauth="onTelegramAuth(user)" data-request-access="write"></script>

Вы можете произвольным образом реализовать функцию onTelegramAuth. Например, послать AJAX запрос на сервер с полученными аргументами.

Получение данных через Redirect

Выберите в конструкторе виджета опцию Authorization Type: Redirect to URL и введите URL, на который вы хотите получить запрос с данными пользователя. Например, введите адрес http://example.com/auth/telegram.

На странице обработки можно положить скрипт index.php следующего содержания:

<?php if (isset($_GET['hash'])) { echo "Logged in as " . $_GET['first_name'] . " " . $_GET['last_name'] . " (" . $_GET['id'] . (isset($_GET['username']) ? ",@" . $_GET['username'] : "") . ")"; } ?>

Проверка данных пользователя

Чтобы удостовериться в правильности полученных данных, нужно проверить hash. Разработчики Telegram приводят пример кода проверки, добавим эту функцию в код из файла index.php

function checkTelegramAuthorization($auth_data) { $check_hash = $auth_data['hash']; unset($auth_data['hash']); $data_check_arr = []; foreach ($auth_data as $key => $value) { $data_check_arr[] = $key . '=' . $value; } sort($data_check_arr); $data_check_string = implode("\n", $data_check_arr); $secret_key = hash('sha256', BOT_TOKEN, true); $hash = hash_hmac('sha256', $data_check_string, $secret_key); if (strcmp($hash, $check_hash) !== 0) { throw new Exception('Data is NOT from Telegram'); } if ((time() - $auth_data['auth_date']) > 86400) { throw new Exception('Data is outdated'); } return $auth_data; }

Разберём механизм работы функции проверки. В качестве аргумента она получает массив с данными пользователя.

array(7) { ["id"]=> string(7) "1831337" ["first_name"]=> string(18) "Александр" ["last_name"]=> string(16) "Менщиков" ["username"]=> string(5) "n0str" ["photo_url"]=> string(36) "https://t.me/i/userpic/100/n0str.jpg" ["auth_date"]=> string(10) "1518168109" ["hash"]=> string(64) "abba<..>1345" }

На первом шаге из массива извлекается значение по ключу hash и сохраняется в переменной.

На втором шаге массив преобразуется к виду key=value и сортируется в лексикографическом порядке. Полученные данные склеиваются в одну строку через разделитель “\n” (код символа – 0xA0).

auth_date=1518168109\nfirst_name=Александр\nid=1831337\nlast_name=Менщиков\nphoto_url=https://t.me/i/userpic/100/n0str.jpg\nusername=n0str

Далее происходит проверка равенства HMAC-SHA-256 подписи этой строки и значения сохранённого hash. Дополнительно проверяется не устарела ли auth_date.

В случае успеха, функция возвращает исходный массив без параметра hash.

Авторизация пользователя на сайте

Добавим в файл код вызова функции проверки

if (isset($_GET['hash'])) { try { $auth_data = checkTelegramAuthorization($_GET); echo "Hello, " . $auth_data['username']; } catch (Exception $e) { die ($e->getMessage()); } }

Пользователь увидит сообщение с приветствием в случае успешной авторизации. Теперь вы можете сохранить информацию о нём в базу данных и привязать его ID к текущей сессии.

Пример кода из рабочего проекта

try { $profile = $tg->checkTelegramAuthorization($_GET); $id = $profile['id']; $user = Model_User::findByAttribute('telegram_id', $id); if ($user->is_empty()) { $user = new Model_User(); $user->telegram_id = $id; ... $user->save() } else { ... } }

Кастомизация кнопки

Сейчас из-за ограничений iframe нельзя изменить внешний вид кнопки. Однако, если возникла сильная необходимость, можно обойти это ограничения с помощью clickjacking.

Внимание! Это решение не рекомендуется к использованию. Clickjacking вводит пользователей в заблуждение, а поисковые системы могут понизить ваш сайт в выдаче в качестве наказания.

Идея состоит в том, чтобы разместить iframe с кнопкой поверх ссылки, оформленной по вашему вкусу. Если такой iframe сделать прозрачным, то пользователь кликнет внутрь него, пытаясь нажать на ссылку.

Итоги

Telegram выпустила полезный инструмент, который позволяет авторизовать пользователей на своём сайте и привязать их профиль к Telegram-аккаунту. К сожалению, пока не поддерживается свободная кастомизация их виджета, а также нет удобного API для аутентификации. Вероятно, в ближайшее время они добавят такие возможности.

Ссылки для подробного изучения