Posterior a de esta larga selección de información hemos podido solucionar esta obstáculo que pueden tener ciertos los lectores. Te compartimos la respuesta y esperamos resultarte de mucha apoyo.
Solución:
Nota (2018-10-23): Ya no uso esto. Demasiada magia sucediendo. En cambio, habilité SOCIALACCOUNT_EMAIL_REQUIRED
y 'facebook': 'VERIFIED_EMAIL': False, ...
. Por lo tanto, allauth redirigirá los inicios de sesión sociales en un formulario de registro social para ingresar una dirección de correo electrónico válida. Si ya está registrado, aparece un error para iniciar sesión primero y luego conectar la cuenta. Lo suficientemente justo para mí cajero automático.
Estoy tratando de mejorar este tipo de caso de uso y se me ocurrió la siguiente solución:
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)
Hasta donde puedo probarlo, parece funcionar bien. ¡Pero las aportaciones y sugerencias son bienvenidas!
Deberá anular el adaptador de sociallogin, específicamente, el pre_social_login
, que se llama después de la autenticación con el proveedor social, pero antes de que este inicio de sesión sea procesado por allauth.
En my_adapter.py
, haz algo como esto
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
Y en su configuración, cambie el adaptador social a su adaptador
SOCIALACCOUNT_ADAPTER = 'myapp.my_adapter.MyAdapter`
Y debería poder conectar varias cuentas sociales a un usuario de esta manera.
Según babus Comente sobre este hilo relacionado, las respuestas propuestas publicadas antes de este (1, 2) introducen un gran agujero de seguridad, documentado en todos los documentos:
“De la documentación de Facebook no está claro si el hecho de que la cuenta esté verificada implica que la dirección de correo electrónico también se verificó. Por ejemplo, la verificación también se puede hacer por teléfono o tarjeta de crédito. Para estar seguro lado, el valor predeterminado es tratar las direcciones de correo electrónico de Facebook como no verificadas “.
Dicho esto, puedo registrarme en Facebook con su ID de correo electrónico o cambiar mi correo electrónico al suyo en Facebook e iniciar sesión en el sitio web para acceder a su cuenta.
Entonces, tomando esto en consideración, y basándose en la respuesta de @sspross, mi enfoque es redirigir al usuario a la página de inicio de sesión y notificarle sobre el duplicado e invitarlo a iniciar sesión con su otra cuenta y vincularlos una vez que han iniciado sesión. Reconozco que difiere de la pregunta original, pero al hacerlo, no se introduce ningún agujero de seguridad.
Por lo tanto, mi adaptador se ve así:
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'))
Aquí puedes ver las reseñas y valoraciones de los usuarios
Eres capaz de añadir valor a nuestra información colaborando tu experiencia en las críticas.