Django allauth social login: автоматическое связывание профилей социальных сайтов с помощью зарегистрированной электронной почты

Я хочу создать самый простой опыт входа возможно для пользователей моего сайта Django. Я представляю себе что-то вроде:

  1. экран входа в систему представлен на
  2. выбрать пользователя для входа в систему с Facebook или Google
  3. пользователь вводит пароль во внешний сайт
  4. пользователь может взаимодействовать с мой сайт как авторизованный пользователь!--6-->

Ok, эта часть проста, просто нужно установить в Django-allauth и настроить его.

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

  1. экран входа в систему представлен на
  2. пользователь выбирает для регистрации
  3. пользователь вводит учетные данные
  4. сайт отправляет письмо с подтверждением
  5. пользователь нажимает на ссылку электронной почты и может взаимодействовать с моим сайтом как аутентифицированный пользователь

Ok, как аутентификация по умолчанию, так и allauth могут это сделать. Но теперь вопрос о миллионе долларов.

если они изменяют, как они делают логин, как я автоматически связать свои учетные записи Google, FB и local?

смотрите, что любой способ входа в систему, у меня есть их адрес электронной почты. Можно ли это сделать с помощью django-allauth? Я знаю, что могу сделать это с вмешательства пользователя. Сегодня поведение по умолчанию-отказаться от входа в систему, сказав, что электронное письмо уже зарегистрировано.

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

для этого есть много причин. Пользователи забудут, какой метод они использовали для аутентификации, и иногда будут использовать Google, Иногда FB, а иногда локальную учетную запись пользователя. У нас уже есть много локальных учетных записей пользователей и учетных записей социальных сетей будет новая функция. Я хочу, чтобы пользователи сохранить свою личность. Я предполагаю возможность запросить список друзей пользователя, поэтому, если они вошли в систему с помощью Google, я хотел бы также иметь их учетную запись FB.

Это сайт хобби, нет больших требований к безопасности, поэтому, пожалуйста, не отвечайте, что это не мудрая реализация безопасности.

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

Я использую Django==1.5.4 и django-allauth==0.13.0

5 ответов


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

на my_adapter.py, сделайте что-нибудь вроде этого

from django.contrib.auth.models import User

from allauth.account.models import EmailAccount
from allauth.exceptions import ImmediateHttpResponse
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter


class MyAdapter(DefaultSocialAccountAdapter):
    def pre_social_login(self, request, sociallogin):
        # This isn't tested, but should work
        try:
            user = User.objects.get(email=sociallogin.email)
            sociallogin.connect(request, user)
            # Create a response object
            raise ImmediateHttpResponse(response)
        except User.DoesNotExist:
            pass

и в настройках измените социальный адаптер на свой адаптер

SOCIALACCOUNT_ADAPTER = 'myapp.my_adapter.MyAdapter`

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


Я пытаюсь улучшить этот вид использования и придумал следующее решение:

from allauth.account.models import EmailAddress
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter

class SocialAccountAdapter(DefaultSocialAccountAdapter):
    def pre_social_login(self, request, sociallogin):
        """
        Invoked just after a user successfully authenticates via a
        social provider, but before the login is actually processed
        (and before the pre_social_login signal is emitted).

        We're trying to solve different use cases:
        - social account already exists, just go on
        - social account has no email or email is unknown, just go on
        - social account's email exists, link social account to existing user
        """

        # Ignore existing social accounts, just do this stuff for new ones
        if sociallogin.is_existing:
            return

        # some social logins don't have an email address, e.g. facebook accounts
        # with mobile numbers only, but allauth takes care of this case so just
        # ignore it
        if 'email' not in sociallogin.account.extra_data:
            return

        # check if given email address already exists.
        # Note: __iexact is used to ignore cases
        try:
            email = sociallogin.account.extra_data['email'].lower()
            email_address = EmailAddress.objects.get(email__iexact=email)

        # if it does not, let allauth take care of this new social account
        except EmailAddress.DoesNotExist:
            return

        # if it does, connect this new social login to the existing user
        user = email_address.user
        sociallogin.connect(request, user)

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


по состоянию на бабусь комментировать это связано потока, предлагаемые ответы выше вводят большую дыру в безопасности, задокументированную в документах allauth:

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

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

поэтому, принимая это во внимание и основываясь на ответе @sspross, мой подход заключается в том, чтобы перенаправить пользователя на страницу входа в систему и уведомить его о дубликате и пригласить его войти в свою другую учетную запись и связать их после входа в систему. Я признаю, что отличается от первоначального вопроса, но при этом не вводится дыра в безопасности.

таким образом, мой адаптер выглядит так:

from django.contrib.auth.models import User
from allauth.account.models import EmailAddress
from allauth.exceptions import ImmediateHttpResponse
from django.shortcuts import redirect
from django.contrib import messages
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter

class MyAdapter(DefaultSocialAccountAdapter):
    def pre_social_login(self, request, sociallogin):
        """
        Invoked just after a user successfully authenticates via a
        social provider, but before the login is actually processed
        (and before the pre_social_login signal is emitted).

        We're trying to solve different use cases:
        - social account already exists, just go on
        - social account has no email or email is unknown, just go on
        - social account's email exists, link social account to existing user
        """

        # Ignore existing social accounts, just do this stuff for new ones
        if sociallogin.is_existing:
            return

        # some social logins don't have an email address, e.g. facebook accounts
        # with mobile numbers only, but allauth takes care of this case so just
        # ignore it
        if 'email' not in sociallogin.account.extra_data:
            return

        # check if given email address already exists.
        # Note: __iexact is used to ignore cases
        try:
            email = sociallogin.account.extra_data['email'].lower()
            email_address = EmailAddress.objects.get(email__iexact=email)

        # if it does not, let allauth take care of this new social account
        except EmailAddress.DoesNotExist:
            return

        # if it does, bounce back to the login page
        account = User.objects.get(email=email).socialaccount_set.first()
        messages.error(request, "A "+account.provider.capitalize()+" account already exists associated to "+email_address.email+". Log in with that instead, and connect your "+sociallogin.account.provider.capitalize()+" account through your profile page to link them together.")       
        raise ImmediateHttpResponse(redirect('/accounts/login'))

Я только что нашел этот комментарий в исходном коде:

        if account_settings.UNIQUE_EMAIL:
            if email_address_exists(email):
                # Oops, another user already has this address.  We
                # cannot simply connect this social account to the
                # existing user. Reason is that the email adress may
                # not be verified, meaning, the user may be a hacker
                # that has added your email address to his account in
                # the hope that you fall in his trap.  We cannot check
                # on 'email_address.verified' either, because
                # 'email_address' is not guaranteed to be verified.

так, это невозможно сделать по дизайну.


если они изменяют способ входа в систему, как мне автоматически связать их учетные записи Google, FB и local?

это возможно, но вы должны быть осторожны, о безопасности. Проверить сценарий:

  1. пользователь создает учетную запись по электронной почте и паролю на вашем сайте. У пользователя нет Facebook.
  2. злоумышленник создает учетную запись на Facebook с электронной почтой пользователя. (Гипотетический сценарий, но вы не контролируете, если социальная сеть проверяет электронная почта.)
  3. злоумышленник войти на ваш сайт с Facebook и автоматически получить доступ к оригинальной учетной записи пользователя.

но вы можете это исправить. Я описываю решение для ticket https://github.com/pennersr/django-allauth/issues/1149

счастливый сценарий должен быть:

  1. пользователь создает учетную запись по электронной почте и паролю на вашем сайте. Пользователь вышел из системы.
  2. пользователь забывает о своей учетной записи и пытается войти через его Фейсбук.
  3. система аутентифицирует пользователя через Facebook и узнает, что он уже создал учетную запись другим способом (электронные письма одинаковы). Система перенаправления пользователя на обычную страницу входа с сообщением " вы уже создали свой аккаунт, используя email и пароль. Пожалуйста, войдите сюда. После входа в систему, вы сможете использовать и войти с помощью Facebook."
  4. вход пользователя по электронной почте и паролю.
  5. система автоматически подключает его логин Facebook с его учетной записью. В следующий раз пользователь может использовать логин Facebook или электронную почту и пароль.