根据对此相关线程的babus评论,在此之前发布的建议答案(1、2)引入了一个很大的安全漏洞,记录在 allauth 文档中:
“从 Facebook 的文档中不清楚,账户经过验证这一事实是否意味着电子邮件地址也经过验证。例如,也可以通过电话或信用卡进行验证。为了安全起见另一方面,默认设置是将来自 Facebook 的电子邮件地址视为未经验证。”
话虽如此,我可以使用您的电子邮件 ID 在 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'))