当检索到的电子邮件与现有用户的电子邮件匹配时,Django allauth Facebook 重定向到注册页面?

27

我成功地使用Django(1.6.4)以及allauth(0.16.1)和Python(2.7)通过Google和Facebook进行登录,并在不存在使用提供程序检索到的电子邮件地址的现有用户的情况下,按预期重定向到settings.LOGIN_REDIRECT_URL。然而,当已存在与从提供者(fb或google)检索到的相同的电子邮件地址的用户时,它总是重定向到/accounts/social/signup/#=注册页面,并询问:

您即将使用您的Facebook/Google账户登录example.com。请完成以下表格作为最后一步: 电子邮件已自动填充。

我尝试了SOCIALACCOUNT_AUTO_SIGNUP = True或False,但没有效果。我尝试更改Facebook的auth_type,但我没有看到除“rerequest”之外的任何选项。

我有以下settings.py设置:

ACCOUNT_AUTHENTICATION_METHOD = "email" # Defaults to username_email
ACCOUNT_USERNAME_REQUIRED = False       # Defaults to True
ACCOUNT_EMAIL_REQUIRED = True           # Defaults to False
SOCIALACCOUNT_QUERY_EMAIL = ACCOUNT_EMAIL_REQUIRED
SOCIALACCOUNT_AUTO_SIGNUP = True
SOCIALACCOUNT_EMAIL_REQUIRED = False
ACCOUNT_ADAPTER = "myproject.adapter.MyLoginAccountAdapter"
LOGIN_URL = "/"
LOGIN_REDIRECT_URL = "/users/{id}/mytags"
我该如何停止这个重定向到注册页面,使提供者登录重定向到已经存在具有相同电子邮件ID的用户的LOGIN_REDIRECT_URL?
注意:我已经尝试了以下方法:
- 我已经更新了我的项目适配器MyLoginAccountAdapter中的get_login_redirect_url。只对谷歌有效,但不能像Django allauth - set facebook redirect所述那样适用于Facebook。 - 我尝试过"Bypass signup form using allauth"但无效。
更新:
- 感谢这个答案,我意识到通过Facebook登录将在以下情况下重定向到注册页面:当从Facebook个人资料检索到的电子邮件与已经存在的用户的电子邮件ID匹配时。 - 我更新了问题以解决上述情况。 - 总结一下问题,这是一个多个提供者帐户具有相同电子邮件ID的情况,django-allauth不允许交替登录(例如,如果我使用Facebook注册过一次,则django-allauth将要求我仅使用Facebook而不是与相同电子邮件ID的任何其他提供者)。 - 我通过使用@receiver(pre_social_login)raise ImmediateHttpResponse(请查看我的答案)解决了这个问题,并附有有用的链接:thisthisone
谢谢, Amit

您想要用户的唯一电子邮件地址吗?如果是,那么这种行为是必需的。 - vibhor
我只想拥有使用相同电子邮件ID交替登录Facebook和Google的能力。我已更新问题。我也找到了答案。请看一下。 - amulllb
你试过 SOCIALACCOUNT_EMAIL_AUTHENTICATION = True 吗?参见社交账户配置 - undefined
2个回答

28

我在深入研究谷歌和Django以及Django-allauth源代码后解决了它。

被解决的问题: 我想要使用相同的电子邮件地址交替使用Facebook和Google进行登录,并始终在成功登录后重定向到LOGIN_REDIRECT_URL,但Django-allauth不允许我这样做。相反,它呈现给我一个我不想要的注册页面。

解决方法:: 使用@receiver(pre_social_login)调用函数link_to_local_user(),先登录然后引发ImmediateHttpResponse,进而重定向到LOGIN_REDIRECT_URL。

#! myproject.adapter.py
from allauth.account.adapter import DefaultAccountAdapter
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
from allauth.exceptions import ImmediateHttpResponse
from allauth.socialaccount.signals import pre_social_login
from allauth.account.utils import perform_login
from allauth.utils import get_user_model
from django.http import HttpResponse
from django.dispatch import receiver
from django.shortcuts import redirect
from django.conf import settings
import json


class MyLoginAccountAdapter(DefaultAccountAdapter):
    '''
    Overrides allauth.account.adapter.DefaultAccountAdapter.ajax_response to avoid changing
    the HTTP status_code to 400
    '''

    def get_login_redirect_url(self, request):
        """ 
        """
        if request.user.is_authenticated():
            return settings.LOGIN_REDIRECT_URL.format(
                id=request.user.id)
        else:
            return "/"


class MySocialAccountAdapter(DefaultSocialAccountAdapter):
    '''
    Overrides allauth.socialaccount.adapter.DefaultSocialAccountAdapter.pre_social_login to 
    perform some actions right after successful login
    '''
    def pre_social_login(self, request, sociallogin):
        pass    # TODOFuture: To perform some actions right after successful login

@receiver(pre_social_login)
def link_to_local_user(sender, request, sociallogin, **kwargs):
    ''' Login and redirect
    This is done in order to tackle the situation where user's email retrieved
    from one provider is different from already existing email in the database
    (e.g facebook and google both use same email-id). Specifically, this is done to
    tackle following issues:
    * https://github.com/pennersr/django-allauth/issues/215

    '''
    email_address = sociallogin.account.extra_data['email']
    User = get_user_model()
    users = User.objects.filter(email=email_address)
    if users:
        # allauth.account.app_settings.EmailVerificationMethod
        perform_login(request, users[0], email_verification='optional')
        raise ImmediateHttpResponse(redirect(settings.LOGIN_REDIRECT_URL.format(id=request.user.id)))


#! settings.py
ACCOUNT_AUTHENTICATION_METHOD = "email" # Defaults to username_email
ACCOUNT_USERNAME_REQUIRED = False       # Defaults to True
ACCOUNT_EMAIL_REQUIRED = True           # Defaults to False
SOCIALACCOUNT_QUERY_EMAIL = ACCOUNT_EMAIL_REQUIRED
SOCIALACCOUNT_AUTO_SIGNUP = True
SOCIALACCOUNT_EMAIL_REQUIRED = False
ACCOUNT_ADAPTER = "myproject.adapter.MyLoginAccountAdapter"
SOCIALACCOUNT_ADAPTER = 'myproject.adapter.MySocialAccountAdapter'
LOGIN_URL = "/"
LOGIN_REDIRECT_URL = "/users/{id}/mytags"

也许您还想尊重“下一个”重定向功能:raise ImmediateHttpResponse(redirect(sociallogin.get_redirect_url(request))) - sspross
2
看起来这种方法仍然存在问题,即通过社交账户登录后,社交账户将无法连接到用户账户。 - Devang
我只使用allauth来处理社交账户,对于本地账户,我使用默认的Django认证系统。所以这个解决方案不适用于我。而且我也希望所有账户(无论是社交账户还是本地账户)都保持唯一的电子邮件地址。顺便问一下,这样做是正确的吗? - Ahtisham
1
我仍然不明白多年后这个库的作者们为什么认为这是一个安全问题。如果你根本不信任OAuth提供者,那么他们为什么要创建这个库呢? - Evan Zamir
1
在旧版的Django中,is_authenticated是一个函数,因此您必须调用它。在最近的Django版本中,它现在是一个属性,您可以删除调用(),只需使用if request.user.is_authenticated:即可。 - sspross
显示剩余4条评论

0

我也遇到过这种情况,原因是再次向Facebook提交access_token以获取基于scope确定的数据时,并未返回任何电子邮件或用户名。

  1. 您需要确保settings.py上的face_book配置至少具有以下范围:'SCOPE': ['email', 'public_profile'],
  2. 前往Facebook开发人员的应用设置,为用户授予电子邮件权限。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接