Авторизация пользователя на вашем сайте через Telegram для Django

  • Tutorial

Привет! 6 февраля Telegram ввел возможность добавлять на свой сайт виджет для авторизации пользователя через его аккаунт в Telegram. Виджеты по виду реализации на сервере делятся на два вида — обработать данные пользователя «здесь и сейчас» в JavaScript или же перенаправить данные в параметрах URL на указанный адрес. Также саму кнопку можно настроить внешне: изменять размер, закругление углов, отключать и включать фотографию.

Материал — руководство по настройке авторизации пользователя через Telegram-аккаунт на вашем сайте с помощью пакета django-telegram-login.

Дисклеймер


django-telegram-login работает для Python 2 и 3 для актуальных версий Django (протестировано на 1.11 и 2.0).

Статья содержит примеры по каждому из пунктов туториала. В конце статьи предоставлены полноценные примеры.

Настройки для Telegram


Рекомендую прочесть новость и документацию по виджетам перед ознакомлением с материалом ниже.

localtunnel


Telegram требует доменное имя для вашего веб-сайта, потому что будет делать запросы на ваш сайт, передавать данные пользователя. Поэтому вам нужен тоннель — веб-сайт в сети, который будет переправлять все запросы на ваш localhost.

Установите пакет localtunnel с помощью npm (убедитесь, что он у вас установлен).

$ npm install -g localtunnel

Запустите localtunnel на порту, на котором вы будете поднимать свой сервер Django.

$ lt --port 8000

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

Запустите сервер и убедитесь, что можете достучаться до сервера через тоннель.

$ ./manage.py runserver 0.0.0.0:8000


Регистрация доменного имени


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


В дальнейшем нам понадобится юзернейм бота (HabrahabrTelegramLoginBot) и его токен (540908629:AAEUGcF_M68nzHmfHtMSAWNncKBVJ75rOZI). На основе токена происходит проверка корректности данных пользователя, которые приходят от Telegram.

Установка и настройки Django


Скачайте пакет через pip следующей командой.

$ pip install django-telegram-login

Добавьте приложение в installed apps.

INSTALLED_APPS = [
    ...
    'django_telegram_login',
]

Структурируйте информацию про бота. Например, добавьте константы в файл настроек (settings.py).

TELEGRAM_BOT_NAME = 'HabrahabrTelegramLoginBot'
TELEGRAM_BOT_TOKEN = '459236585:AAEee0Ba4fRijf1BRNbBO9W-Ar15F2xgV98'
TELEGRAM_LOGIN_REDIRECT_URL = 'https://kqmxkdqitb.localtunnel.me/'

И используйте их в таком виде.

from django.conf import settings


bot_name = settings.TELEGRAM_BOT_NAME
bot_token = settings.TELEGRAM_BOT_TOKEN
redirect_url = settings.TELEGRAM_LOGIN_REDIRECT_URL

django-telegram-login позволяет использовать неограниченное количество ботов для авторизации в Telegram. Виджеты не берут параметры бота из настроек, а принимают их в себя как аргументы. Поэтому нет проблем конструировать много разных виджетов.

Как работает виджет


Виджет имеет следующий вид (рисунок внизу). Вы можете изменять его размеры — маленький (small), средний (medium) и большой (large), закруглять углы (corner_radius), убирать фотографию (disable user photo). Вы можете указать тип обработки авторизации пользователя — обработать данные пользователя «здесь и сейчас» в JavaScript или же отправить в параметрах URL на указанный адрес. Документация по конфигурациям виджетов Telegram доступна здесь. django-telegram-login уже имеет внутри себя все необходимое для конфигурации виджетов в коде.


(размер картинки отличается от оригинальных размеров виджета)

Виджет


Виджет — это внешний JavaScript, который принимает ваши аргументы (data-size — размер виджета, например), обрабатывает их и отвечает данными пользователя при корректной работе. Также он генерирует «кнопку» Log in as.

<script async src="https://telegram.org/js/telegram-widget.js?2" data-telegram-login="samplebot" data-size="large" data-auth-url="" data-request-access="write">
</script>

(Пример виджета с документации Telegram)

Как настроить виджет используя django-telegram-login

Вам не нужно заходить в документацию Telegram, чтобы конфигурировать скрипт и каждый раз копировать его в вашу HTML-страницу. Используйте генератор виджетов от django-telegram-login.

from django.conf import settings

from django_telegram_login.widgets.constants import (
    SMALL, 
    MEDIUM, 
    LARGE,
    DISABLE_USER_PHOTO,
)
from django_telegram_login.widgets.generator import (
    create_callback_login_widget,
    create_redirect_login_widget,
)


bot_name = settings.TELEGRAM_BOT_NAME
bot_token = settings.TELEGRAM_BOT_TOKEN
redirect_url = settings.TELEGRAM_LOGIN_REDIRECT_URL

telegram_login_widget = create_callback_login_widget(bot_name, corner_radius=10, size=MEDIUM)

telegram_login_widget = create_redirect_login_widget(
    redirect_url, bot_name, size=LARGE, user_photo=DISABLE_USER_PHOTO
)

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

corner_radius


Опциональный параметр corner_radius принимает целочисленное число от 1 до 20 (это значение по умолчанию). Прямоугольная — 1, закругленная — 20.

user_photo


С помощью константы DISABLE_USER_PHOTO, переданной в параметр user_photo, можно отключить фотографию пользователя возле виджета.

create_callback_login_widget и create_redirect_login_widget, генераторы виджетов, принимают эти константы и на их основе генерируют код для виджета по вашим требованиям. Давайте посмотрим, что они возвращают в переменных telegram_login_widget.

Как мы обсуждали выше, виджет — это всего лишь внешний JavaScript. Поэтому telegram_login_widget в случае callback вернет следующее.

<script async src="https://telegram.org/js/telegram-widget.js?2" data-telegram-login="HabrahabrTelegramLoginBot" data-size="medium" data-radius="10" data-auth-url="https://kqmxkdqitb.localtunnel.me" data-onauth="onTelegramAuth(user)" data-request-access="write"></script>

Как видите, генератор виджета подставил в скрипт юзернейм вашего бота, размер виджета и ссылку на ваш сайт. Функцию onTelegramAuth в data-onauth рассмотрим ниже.

Логика конструирования виджетов


На примере функции create_redirect_login_widget, которая знает как строить виджет, можно увидеть — в тело виджета вставляются переданные в нее переменные redirect_url, bot_name и user_photo.

def create_redirect_login_widget(
        redirect_url, bot_name, size=SMALL, user_photo=True, access_write=True
):
    """
    Create redirect widget, that allows to handle user data as get request params.
    """
    script_initital = \
        '<script async src="https://telegram.org/js/telegram-widget.js?2" '
    bot = 'data-telegram-login="{}" '.format(bot_name)
    size = 'data-size="{}" '.format(size)
    userpic = \
        'data-userpic="{}" '.format(str(user_photo).lower()) if not user_photo else ''
    redirect = 'data-auth-url="{}" '.format(redirect_url)
    access = 'data-request-access="write"' if access_write else ''
    script_end = '></script>'

    widget_script = \
        script_initital + bot + size + userpic + redirect + access + script_end
    return widget_script

На выходе получается «сырой текст», который можно отобразить в HTML-шаблоне.

Отображение сгенерированного скрипта на HTML странице


Сгенерируйте ваш виджет в какой-нибудь view и передайте в контекст.

def callback(request):
    telegram_login_widget = create_callback_login_widget(bot_name, size=SMALL)

    context = {'telegram_login_widget': telegram_login_widget}
    return render(request, 'telegram_auth/callback.html', context)

В самом шаблоне HTML используйте переменную telegram_login_widget из context с помощью Jinja (типа того).

It is template to render callback Telegram widget! 

<!-- {{ telegram_login_widget|safe }} --> {% autoescape off %}
{{ telegram_login_widget }}{% endautoescape %} 

<script type="text/javascript"> 
    function onTelegramAuth(user) { 
        alert('Logged in as ' + user.first_name + ' ' + user.last_name + '!'); 
    } 
</script>

Тег autoescape используется, чтобы «сказать» HTML отобразить telegram_login_widget как разметку, а не как сырой текст. В итоге на странице отобразится следующее.


Пользователь жмет на кнопку, получает запрос на подтверждение авторизации.


Если пользователь делает это первый раз, сначала появится авторизация в Telegram.


После этого и в дальнейших нажатиях на кнопку Log in as — Telegram будет высылать данные пользователя каждый раз, пока пользователь не отключится в чате c Telegram (если отключится, придется подтверждать вход еще раз).


В функцию onTelegramAuth попадает ответ от Telegram с данными пользователя.
Используйте аргумент user, чтобы получить данные.

<script type="text/javascript"> 
    function onTelegramAuth(user) { 
        alert('Logged in as ' + user.first_name + ' ' + user.last_name + '!'); 
    } 
</script>


Telegram возвращает следующие данные: first_name, last_name, username, photo_url, auth_date (unix datetime) and hash (два последних значения технические, для верификации). Можно реализовать передачу данных от onTelegramAuth на вашу view через AJAX запрос.

redirect виджет поле нажатия на кнопку Log as in перенаправляет пользователя на указанную ссылку (TELEGRAM_LOGIN_REDIRECT_URL) и вставляет в параметры запроса (параметры ссылки) данные пользователя.


Что касается этого типа виджета — вам необходимо пройти аутентификацию данных.

from django_telegram_login.authentication import verify_telegram_authentication
from django_telegram_login.errors import (
    NotTelegramDataError, 
    TelegramDataIsOutdatedError,
)


def index(request):

    # Вы можете перенаправлять пользователя на главную, но предусмотрите вариант,
    # что обычные пользователи также могут быть на главной и обработайте исключение,
    # что данных от Telegram в параметрах запроса нет.
    if not request.GET.get('hash'):
        return HttpResponse('Handle the missing Telegram data in the response.')

    try:
        result = verify_telegram_authentication(
             bot_token=bot_token, request_data=request.GET
        )

    except TelegramDataIsOutdatedError:
        return HttpResponse('Authentication was received more than a day ago.')

    except NotTelegramDataError:
        return HttpResponse('The data is not related to Telegram!')

    return HttpResponse('Hello, ' + result['first_name'] + '!')

Функция verify_telegram_authentication на основе полученных данных из параметров запроса и токена бота верифицирует корректность данных (проверяет, что вы не получили не существующие или фальшивые данные). Алгоритм аутентификации описан здесь.

Примеры


Все необходимые настройки — settings.py

TELEGRAM_BOT_NAME = 'django_telegram_login_bot'
TELEGRAM_BOT_TOKEN = '459236585:AAEee0Ba4fRijf1BRNbBO9W-Ar15F2xgV98'
TELEGRAM_LOGIN_REDIRECT_URL = 'https://iyjjvnvszx.localtunnel.me/'

Так может выглядеть ваш файл views.py с полной реализацией авторизации пользователя через Telegram.

from django.conf import settings
from django.http import HttpResponse
from django.shortcuts import render

from django_telegram_login.widgets.constants import (
    SMALL, 
    MEDIUM, 
    LARGE,
    DISABLE_USER_PHOTO,
)
from django_telegram_login.widgets.generator import (
    create_callback_login_widget,
    create_redirect_login_widget,
)
from django_telegram_login.authentication import verify_telegram_authentication
from django_telegram_login.errors import (
    NotTelegramDataError, 
    TelegramDataIsOutdatedError,
)

bot_name = settings.TELEGRAM_BOT_NAME
bot_token = settings.TELEGRAM_BOT_TOKEN
redirect_url = settings.TELEGRAM_LOGIN_REDIRECT_URL


def index(request):

    # Initially, the index page may have no get params in URL
    # For example, if it is a home page, a user should be redirected from the widget
    if not request.GET.get('hash'):
        return HttpResponse('Handle the missing Telegram data in the response.')

    try:
        result = verify_telegram_authentication(
            bot_token=bot_token, request_data=request.GET
        )

    except TelegramDataIsOutdatedError:
        return HttpResponse('Authentication was received more than a day ago.')

    except NotTelegramDataError:
        return HttpResponse('The data is not related to Telegram!')

    # Or handle it like you want. For example, save to DB. :)
    return HttpResponse('Hello, ' + result['first_name'] + '!')


def callback(request):
    telegram_login_widget = create_callback_login_widget(bot_name, size=SMALL)

    context = {'telegram_login_widget': telegram_login_widget}
    return render(request, 'telegram_auth/callback.html', context)


def redirect(request):
    telegram_login_widget = create_redirect_login_widget(
        redirect_url, bot_name, size=LARGE, user_photo=DISABLE_USER_PHOTO
    )

    context = {'telegram_login_widget': telegram_login_widget}
    return render(request, 'telegram_auth/redirect.html', context)

Шаблон для callback логина — callback.html

It is template to render callback Telegram widget!

<!-- {{ telegram_login_widget|safe }} -->
{% autoescape off %}{{ telegram_login_widget }}{% endautoescape %}

<script type="text/javascript">
    function onTelegramAuth(user) {
        alert('Logged in as ' + user.first_name + ' ' + user.last_name + '!');
    }
</script>

Шаблон для redirect логина — redirect.html

It is template to render redirect Telegram widget!

<!-- {{ telegram_login_widget|safe }} -->
{% autoescape off %}{{ telegram_login_widget }}{% endautoescape %}

Спасибо за внимание. Отзывы и предложения присылайте на личную почту. Если вы заинтересовались django-telegram-login, заходите на страницу проекта на Github.
Поделиться публикацией
Похожие публикации
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 12
  • +5
    Вам, прежде чем плодить кучу бесполезных пакетов и тонны бездарно написанного кода, стоит остыть и немного подучиться. А то глазами пробежался — можно тыкать, как котенка в ковер. Нет HTTP кодов ответа, «шикарный» нейминг, неидиоматичный код, больше на PHP похожий. И самое страшное, это же кто-то все же попытается использовать в продакшене. Я, честно говоря, еще не задавался вопросом, можно ли это реализовать как бэкенд для аутентификации в Django, но, я думаю, и автор тоже.
    • 0
      Здравствуйте.

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

      Вам нужно было не пробегаться, а изучить домэйн немного глубже. О каких HTTP статус-кодах речь, если Телеграм нам ничего не возвращает? Мы ему кормим ссылку на наш веб-сайт, а он уже сам делает запрос на наш сервер (например, как сделано с виджетом на редирект).

      Что касается нейминг также не соглашусь. Например, verify_telegram_authentication или create_callback_login_widget максимально приближенные к названиям в документации Телеграм, как и переменные, строящие строку виджета.

      Претензии на идиоматичный код мне также не ясны. Код алгоритм аутентификации построен на инструкциях из документации. Остальной код (не больше 50 строк от силы) содержит банальные конструкции, похожие во всех языках.

      Спасибо большое, что оставили минус с комментарием — навык, которого многим не хватает. Если бы вы разъяснили по недовольствам подробнее, чтобы я смог испрувнуть свои навыки, я был бы рад.
      • +1
        А что тут разъяснять? Начнем с самого сомнительного — с функционала. Как известно, кастомная аутентификация реализуется в джанге через бэкенды. Желательно еще не поломать работу админки и декораторов типа @login_required, модель юзера и прочее. Я уже молчу про сессии. Где? Итак, функционал определились — отсутствует. На этом можно закончить. Качество кода я оставлю за рамками, а то скажете, придираюсь.
        • 0
          Рандом не совсем прав по форме (но здесь всё же место общения специалистов, а не сайт знакомств), но абсолютно прав по содержанию.

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

          Чтобы сравнять баланс, коли вас это настолько (слишком сильно для специалиста) задевает — добавил в избранное и подписался на вас лично. А теперь давайте уже к делу.

          Успехов!
      • 0

        Кстати, городить проксю из домена не обязательно. Боту можно задать http://127.0.0.1/. Единственное ограничение — это то что надо Django проект на 80 порту запускать (скорее всего вам понадобится привилегированный пользователь). Все будет работать.

        • 0
          django-telegram-login работает для Python второй и третьей версии и Django <1.11 и >2.0 соответственно.


          А что не так с 1.11 и 2.0? Почему они не поддерживаются?
          • –1
            а то, что там версии указаны меньше 1.11и больше 2.0, вас не смутило?
            • –1
              Здравствуйте,

              конечно, поддерживаются. Забыл добавить равно. :)
              Спасибо, исправлено.
            • 0
              Кто работает с Telegram Login Widget скриптом через 127.0.0.1 нужен только 80 порт или изнасилуете моск и убьете время как я. Пол дня убил тк нода работала на 3000 порту. Спсб lepism за
              проект на 80 порту запускать

              • 0
                А что представляет из себя id который возвращает телега? ID пользователя?
                • 0
                  Привет, а как получить фото юзера? через бек или фронт данных по аватару нету
                  • 0
                    Привет.

                    В данных от Телеграма должна придти ссылка на аватар. Не приходит?

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.