Закрыть

Для эффективной работы на сайте используются cookie и обработка персональных данных. Пользуясь этим сайтом, вы соглашаетесь с правилами использования сайта. Подробнее

Цитата дня

Vivazzi.ru

Личный сайт Мальцева Артема

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

Дейл Карнеги

Django allauth Авторизация через социальную сеть

16 октября 2017 г. 23:04

Django-allauth замечательный пакет для авторизации пользователей на своём сайте, который берёт на себя рутинные операции по регистрации пользователей обычным способом через форму, а также через популярные социальные сети.

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

На данный момент приложение поддерживает более 60 соц. сетей (провайдеров).

Кроме типичных полей Имя, Фамилия, Email и Пароль вы легко сможете добавить дополнительные поля. Также можно легко написать свою авторизацию через соц. сеть, если ее нет в списке поддерживаемых, так как провайдеры описаны через единый интерфейс, то есть уснаследуются от одного класса OAuth2Provider или OAuthProvider, который обрабатывает протокол авторизации OAuth 2 или OAuth соответственно.

Хорошо, что Django-allauth предоставляет возможность добавлять несколько email-ов пользователей и указывать основной. Бывает, что пользователь вдруг теряет контроль над одной какой-нибудь своей электронной почтой и тут приходят на помощь резервные email-ы, на которые могут прийти пароли или важные письма с вашего сайта.

Список всех преимуществ вы найдёте в официальной документации http://django-allauth.readthedocs.io/en/latest/index.html

Доверенный redirect URI

Во многих соц. сетях при добавлении приложения спрашивают про некий Доверенный redirect URI (или Действительные URL-адреса для перенаправления OAuth) например в ВК:

Доверенный redirect URI

Доверенный redirect URI нужен для дополнительной защиты приложения от утечек токенов зарегистрировавшихся пользователей в следствие хакерских атак.

В django-allauth Доверенный redirect URI строится по следующему принципу:

http://[domain]/accounts/[provider]/login/callback/

где [domain] - домен сайта, а [provider] - социальная сеть (приложение) типа vk, google, facebook и т. д.

В некоторых приложениях, в том числе и в ВКонтакте, есть возможность протестировать авторизацию локально на девелоперской машине. Для этого обычно в настройки нужно добавить localhost в Базовые домены и http://localhost:8000/accounts/[provider]/login/callback/ в Доверенный redirect URI.

Простейший пример использования django-allauth

Устанавливаем и настраиваем django-allauth согласно документации http://django-allauth.readthedocs.io/en/latest/installation.html . Попробуем добавить авторизацию через ВКонтакте. Для начала нужно зарегистрировать своё приложение в VK .

Замечание
В разделе http://django-allauth.readthedocs.io/en/latest/providers.html размещены поддерживаемые провайдеры и ссылки на документацию по регистрации приложения в соответствующей соц. сети.

Затем нужно убедиться, что провайдер VK добавлен в INSTALLED_APPS. Мои настройки в settings.py для django-allauth выглядят так:

SITE_ID = 1

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                ...
                'django.template.context_processors.request',
            ],
        },
    },
]

AUTHENTICATION_BACKENDS = (
    ...
    'django.contrib.auth.backends.ModelBackend',
    'allauth.account.auth_backends.AuthenticationBackend',
)

INSTALLED_APPS = (
    ...
    # The following apps are required:
    'django.contrib.auth',
    'django.contrib.sites',

    'allauth',
    'allauth.account',
    'allauth.socialaccount',

    # ... include the providers you want to enable:
    'allauth.socialaccount.providers.vk',
    ...
)

В шаблоне:

{% load socialaccount %}
<a href="{% provider_login_url "vk" method="oauth2" %}"> Войти через VK </a>

Для вывода всех подключённых провайдеров, вы можете использовать следующий код:

{% load socialaccount %}
{% get_providers as socialaccount_providers %}

{% for provider in socialaccount_providers %}
    {% if provider.id == "openid" %}
        {% for brand in provider.get_brands %}
          <a title="{{brand.name}}" class="{{provider.id}} {{brand.id}}" href="{% provider_login_url provider.id openid=brand.openid_url process=process %}">{{brand.name}}</a>
        {% endfor %}
    {% endif %}
    <a title="{{provider.name}}" class="{{provider.id}}" href="{% provider_login_url provider.id process=process scope=scope auth_params=auth_params %}">{{provider.name}}</a>
{% endfor %}

И наконец, нужно связать полученные при регистрации приложения client_ID и секретный ключ с сайтом. Для этого в админке перейдите в Социальные приложения и добавьте социальное приложение:

Добавление социального приложения в django-allauth

Часто используемые настройки django-alluath

На всех сайтах я использую авторизацию через email и пароль, но в то же время без необходимости заполнять логин, поэтому я использую следующие настройки:

ACCOUNT_EMAIL_REQUIRED = True  
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_AUTHENTICATION_METHOD = 'email'

Количество попыток входа в систему я оставляю по умолчанию (равное 5), но время таймаута ограничиваю до 60 секунд:

ACCOUNT_LOGIN_ATTEMPTS_TIMEOUT = 60

Часто требуются дополнительные поля при регистрации пользователей, поэтому я создаю дополнительную форму:

class CustomSignupForm(forms.Form):
    first_name = forms.CharField(max_length=30, widget=forms.TextInput(attrs={'placeholder': _('First name')}))
    last_name = forms.CharField(max_length=30, widget=forms.TextInput(attrs={'placeholder': _('Last name')}))

    GENDERS = (('man, _('Man')), ('woman', _('Woman')))
    gender = forms.ChoiceField(label=_('Gender'), choices=GENDERS, widget=forms.Select())

    def signup(self, request, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.gender = self.cleaned_data['gender']

        user.save()

А затем подключаю её в settings.py:

ACCOUNT_SIGNUP_FORM_CLASS = 'spec.forms.CustomSignupForm'

Теперь при выводе формы в шаблоне, мы увидим не только базовые поля (email и пароль), но и свои дополнительные поля. Получить такую форму в коде можно так:

# -*- coding: utf-8 -*-
from allauth.account.forms import LoginForm, SignupForm


def show_auth(request):
    return render(request, 'auth.html', {'singup_form': SignupForm(), 'login_form': LoginForm()})

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

# settings.py
SOCIALACCOUNT_AUTO_SIGNUP = False

И очень полезная настройка - кастомный редирект после входа на сайт:

# settings.py
ACCOUNT_ADAPTER = 'project.users.adapter.MyAccountAdapter'

# spec/adapter.py
from django.conf import settings
from allauth.account.adapter import DefaultAccountAdapter

class MyAccountAdapter(DefaultAccountAdapter):

    def get_login_redirect_url(self, request):
        return '/users/{}/'.format(request.user.username)

Ошибка resp.json()['response'][0] KeyError: 'response' для VK провайдера

Данная ошибка возникает в версиях allauth ниже 0.36.0 из-за того, что ВКонтакте изменили api, сделав параметр v обязательным (v - версия api vk).

1-ый вариант решения: обновить allauth до версии 0.36.0

В allauth версии 0.36.0 этот параметр добавили, поэтому обновите allauth до указанной версии и всё должно заработать.

2-ой вариант решения: переопределить VK провайдер

Вы можете и другим способом решеть проблему, добавив свой кастомный VK провайдер. Для этого нужно скопировать провайдер vk из приложения alluath (находится в allauth/socialaccount/providers/vk/) куда-нибудь в своё приложение, например, spec/providers/vk.

Добавляем в файле spec/providers/vk/views.py параметр 'v': '3.0', в словарь params в функции complete_login класса VKOAuth2Adapter. Полный листинг приведён ниже:

import requests

from allauth.socialaccount.providers.oauth2.views import (
    OAuth2Adapter,
    OAuth2CallbackView,
    OAuth2LoginView,
)

from .provider import VKProvider

USER_FIELDS = ['first_name',
               'last_name',
               'nickname',
               'screen_name',
               'sex',
               'bdate',
               'city',
               'country',
               'timezone',
               'photo',
               'photo_medium',
               'photo_big',
               'photo_max_orig',
               'has_mobile',
               'contacts',
               'education',
               'online',
               'counters',
               'relation',
               'last_seen',
               'activity',
               'universities']


class VKOAuth2Adapter(OAuth2Adapter):
    provider_id = VKProvider.id
    access_token_url = 'https://oauth.vk.com/access_token'
    authorize_url = 'https://oauth.vk.com/authorize'
    profile_url = 'https://api.vk.com/method/users.get'

    def complete_login(self, request, app, token, **kwargs):
        uid = kwargs['response'].get('user_id')
        params = {
            'v': '3.0',
            'access_token': token.token,
            'fields': ','.join(USER_FIELDS),
        }
        if uid:
            params['user_ids'] = uid
        resp = requests.get(self.profile_url,
                            params=params)
        resp.raise_for_status()
        extra_data = resp.json()['response'][0]
        email = kwargs['response'].get('email')
        if email:
            extra_data['email'] = email
        return self.get_provider().sociallogin_from_response(request,
                                                             extra_data)


oauth2_login = OAuth2LoginView.adapter_view(VKOAuth2Adapter)
oauth2_callback = OAuth2CallbackView.adapter_view(VKOAuth2Adapter)

Затем в INSTALLED_APPS строчку 'allauth.socialaccount.providers.vk', заменить на 'spec.providers.vk',

Теперь должно работать.

Оцените статью

4,8 из 5 (всего 5 оценок)

Поля, отмеченные звёздочкой ( * ) , являются обязательными.

Спасибо за ваш отзыв!

Автор статьи

Права на использование данной статьи, расположенной на настоящей странице http://vivazzi.ru/it/allauth/:

Разрешается копировать статью с указанием её автора и ссылки на оригинал без использования параметра rel="nofollow" в теге <a>. Использование:

Автор статьи: Мальцев Артём
Ссылка на статью: <a href="http://vivazzi.ru/it/allauth/">http://vivazzi.ru/it/allauth/</a>

Подробнее: Правила использования сайта

Вам нужно саморазвиваться или вы хотите зарабатывать деньги?

Или вы ищите хорошие IT сервисы или книги? Сохраните свое время и взгляните на мою подборку рекомендаций, которыми постоянно пользуюсь.
Посмотреть рекомендации

Комментариев: 14

Goodwin Oz
Goodwin Oz

04.04.2018 9:16 #

Третий день мучаюсь, не понимаю в чем может быть ошибка "KeyError at /accounts/vk/login/callback/ 'response'" не подскажите в чем может быть проблема?

Ответить

Артём Мальцев
Артём Мальцев автор

05.04.2018 4:26 #

Покажите полный трейсбек.
А вообще похоже в данных (которые приходят на url /accounts/vk/login/callback/) нет ключа response. Нужно посмотреть, какие данные приходят с этого урла. Скорее всего error какой-нибудь.

Ответить

Goodwin Oz
Goodwin Oz

05.04.2018 8:24 #

Internal Server Error: /accounts/vk/login/callback/
Traceback (most recent call last):
  File "C:\Development\my_blog\blog\lib\site-packages\django\core\handlers\exception.py", line 41, in inner
    response = get_response(request)
  File "C:\Development\my_blog\blog\lib\site-packages\django\core\handlers\base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "C:\Development\my_blog\blog\lib\site-packages\django\core\handlers\base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Development\my_blog\blog\lib\site-packages\allauth\socialaccount\providers\oauth2\views.py", line 73, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Development\my_blog\blog\lib\site-packages\allauth\socialaccount\providers\oauth2\views.py", line 134, in dispatch
    response=access_token)
  File "C:\Development\my_blog\blog\lib\site-packages\allauth\socialaccount\providers\vk\views.py", line 53, in complete_login
    extra_data = resp.json()['response'][0]
KeyError: 'response'
[05/Apr/2018 15:23:19] "GET /accounts/vk/login/callback/?code=e56a6eac81244665a6&state=QmdSrELVWbeO HTTP/1.1" 500 85411

Ответить

Артём Мальцев
Артём Мальцев автор

05.04.2018 22:17 #

Да, я был прав, всё-таки в ответе resp.json() нет ключа 'response'. Что-то другое приходит (скорее всего error). Попробуйте подебажить строку: File "C:\Development\my_blog\blog\lib\site-packages\allauth\socialaccount\providers\vk\views.py", line 53, - посмотрите, что возвращает resp.json() - поймёте, что за ошибка.

Скорее всего ваша ошибка связана с самой настройкой приложения. Вот попробуйте почитать этот материал : https://vivazzi.ru/it/auth-vk/

Ответить

Goodwin Oz
Goodwin Oz

06.04.2018 2:40 #

Спасибо вам огромное, за советы. Просто видимо я ещё не дорос до этого и пока придётся обходиться джанговской авторизацией, без социальных аккаунов, в ней хотя бы разобрался. :)

Ответить

Артём Мальцев
Артём Мальцев автор

08.04.2018 20:53 #

Пожалуйста, рад помочь!
Если есть вопрос как дебажить, вот попробуйте посмотреть https://www.youtube.com/watch?v=QJtWxm12Eo0 - рассказывает о дебаге в PyCharm.
Удачи вам в освоении!

Ответить

Артём Мальцев
Артём Мальцев автор

11.05.2018 0:48 #

Вот решение вашей проблемы: https://vivazzi.ru/it/allauth/#comment_128

Ответить

Кирилл
Кирилл

08.04.2018 15:33 #

А что делать если локально получается авторизоваться, а при деплое - нет.
Проблема в редиректе на callback - этот колбек отсылает снова авторизоваться в соц.сеть.
Проблема наблюдается с vk и instagram и только на production сервере

Ответить

Артём Мальцев
Артём Мальцев автор

08.04.2018 20:58 #

Странное поведение. В Instagram не подскажу, а вот в вк: вы добавили два базовых домена (напр., localhost и yoursite.ru?) и настроили два callback-а (http://localhost:8000/accounts/vk/login/callback/ и http://yoursite.ru/accounts/vk/login/callback/) в настройках приложения? Смотрели https://vivazzi.ru/it/auth-vk/ ? я там подробно описал авторизацию через ВК.

Ответить

Кирилл
Кирилл

09.04.2018 1:52 #

Решилось, просто обновить библиотеку на продакшене нужно было. Спасибо

Ответить

Гость
Гость

27.04.2018 7:37 #

Не работает авторизация через vk вообще ни через виджет ни средствами DLE. Страница перезагружается, авторизации не происходит

Ответить

Артём Мальцев
Артём Мальцев автор

29.04.2018 10:13 #

ВКонтакте изменили api, сделав параметр v обязательным (v - версия api vk).
В allauth версии 0.36.0 этот параметр добавили, поэтому обновите allauth до указанной версии и всё будет работать.
Также можно попробовать другой способ, описанный в разделе Ошибка resp.json()['response'][0] KeyError: 'response' для VK провайдера в данной статье.

Ответить

Гость
Гость

01.05.2018 9:01 #

К посту выше: вот такая ошибка появляется в resp.json() {'error': {'error_code': 8, 'error_msg': 'Invalid request: v (version) is required',

Ответить

Артём Мальцев
Артём Мальцев автор

02.05.2018 22:14 #

Да, всё верно. Попробуйте как я описал в посте https://vivazzi.ru/it/allauth/#comment_128
Это должно помочь

Ответить

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

Чтобы оставить комментарий от своего имени войдите или зарегистрируйтесь обычным способом или через социальные сети:

Отправить

На данный момент нет специального поиска, поэтому я предлагаю воспользоваться обычной поисковой системой, например, Google, добавив "vivazzi" после своего запроса.

Попробуйте