Подтвердить что ты не робот

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

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

  • Экран входа в систему представлен пользователю
  • Пользователь выбирает логин с Facebook или Google
  • Пользователь вводит пароль на внешнем сайте
  • Пользователь может взаимодействовать с моим сайтом в качестве аутентифицированного пользователя.

Хорошо, эта часть проста, просто установите django-allauth и настройте ее.

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

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

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

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

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

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

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

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

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

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

4b9b3361

Ответ 1

Вам потребуется переопределить адаптер 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`

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

Ответ 2

Примечание (2018-10-23): я больше этим не пользуюсь. Слишком много волшебства происходит. Вместо этого я включил SOCIALACCOUNT_EMAIL_REQUIRED и 'facebook': { 'VERIFIED_EMAIL': False,... }. Таким образом, allauth перенаправит социальные логины в форму для регистрации в социальных сетях, чтобы ввести действительный адрес электронной почты. Если он уже зарегистрирован, сначала появляется ошибка для входа в систему, а затем для подключения учетной записи. Достаточно справедливо для меня, атм.


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

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 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)

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

Ответ 3

Согласно комментарию бабуса к этой связанной теме, предложенные ответы, опубликованные перед этим (1, 2), вводят большую дыру в безопасности, задокументированную в документах Аллаута:

"Из документации 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 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'))

Ответ 4

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

        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.

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

Ответ 5

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

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

  • Пользователь создает учетную запись по электронной почте и паролю на вашем сайте. Пользователь не имеет Facebook.
  • Атакующий создает учетную запись на Facebook с электронной почтой пользователя. (Гипотетический сценарий, но вы не контролируете, если в социальной сети проверяется электронная почта).
  • Нападающий заходит на ваш сайт с Facebook и автоматически получает доступ к исходной учетной записи пользователя.

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

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

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

Ответ 6

Я хочу немного отодвинуться назад в вопросах безопасности, особенно когда вы используете провайдеров, которые разрешают только проверенные электронные письма. На моем сайте пользователи не могут создавать свои собственные учетные записи. Существующие пользователи (с достаточными привилегиями) должны создавать для них учетные записи. Я не хочу, чтобы только что созданным пользователям приходилось устанавливать новый пароль, если у них есть электронная почта Google Apps. Я хочу, чтобы они вошли в систему с помощью Google Oauth. Похоже, что переопределение pre_social_login, как предложено выше, будет решением для меня.