New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature request: Automatically connecting an account to an already registered email #418
Comments
I agree with you that allauth should not be too opiniated and make life difficult for you want you want to move forward and implement your requirements. Whether or not this is something that needs to be supported out of the box needs a bit more thought though. The way I see it is that only if all of the following conditions hold, then the login can be shortcircuited:
Google clearly indicates that e-mail addresses are verified. For Facebook this is not so clear -- the account is said to be verified (which could mean you verified your phone number, not your e-mail address). For Twitter this will not work at all -- no e-mail address is handed over here. As for the implementation, there is a |
A few points:
I am not sure yet if and how I would like to have this functionality end up in allauth. For one, I am not convinced it is a good idea to implicitly hookup the FB account to his local account without at least informing the user what is going on. Furthermore, if something like this is implemented it could take situations where there is no email verified into account as well. All in all, for now I suggest that you implement this using the |
Ok, I'll try to implement it using Excuse me, I was wrong. The verified_email was from Google provider, not Facebook. The scenario 3 is if the user is authenticated from Google, but need to get his friend list from Facebook so he can use a specific site feature. I'd ask him to login to Facebook so he can use the feature. His identity would stay the same for the site. |
Ok, but then I think your scenario 3 is already supported. Simply use something like this:
|
I am in in the similar situation where I want the verified status to set the verified email to true. Perhaps this is more of a stack question though I seem to not be getting any traction there. |
What does one have to do in the |
@gjcourt I am not sure what you are referring to by "make this work". |
@pennersr I figured it out, should probably have this as an example of "auto login" class SocialAdapter(DefaultSocialAccountAdapter):
def is_auto_signup_allowed(self, request, sociallogin):
return True
def pre_social_login(self, request, sociallogin):
user = sociallogin.account.user
if user.id:
return
try:
existing_user = User.objects.get(email=user.email)
except ObjectDoesNotExist:
pass
else:
perform_login(request, existing_user, app_settings.EmailVerificationMethod.NONE) |
+1 for this feature. In the mean time, trying the pre hook similar to the above per stackoverflow. |
hey guys, don't know if I missed something, but this is still an issue for me and I came up with this solution. is this totally wrong and/or are there better solutions? thanks you! 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) |
@sspross, your approach doesn't consider whether email addresses have been verified. Before dynamically linking social accounts to users in this way, I'd recommend at least ensuring that email addresses are verified. Unless of course the system you're working on doesn't merit this best practice. I'd consider something like: def pre_social_login(self, request, sociallogin):
# social account already exists, so this is just a login
if sociallogin.is_existing:
return
# some social logins don't have an email address
if not sociallogin.email_addresses:
return
# find the first verified email that we get from this sociallogin
verified_email = None
for email in sociallogin.email_addresses:
if email.verified:
verified_email = email
break
# no verified emails found, nothing more to do
if not verified_email:
return
# check if given email address already exists as a verified email on
# an existing user's account
try:
existing_email = EmailAddress.objects.get(email__iexact=email.email, verified=True)
except EmailAddress.DoesNotExist:
return
# if it does, connect this new social login to the existing user
sociallogin.connect(request, existing_email.user) |
@lukeburden we consider email addresses from social accounts as verified. or in other words, we just trust our social login partners to verify the users "good enough". if a user creates a normal account, we confirm the email address of course. but you are right, this way we've to be careful which providers we turn on. |
@lukeburden's solution was the best way for me. |
@deepai-org, @lukeburden how did you change the behavior in your own code? Did you create a local fork of the code? |
Using Google Login, the
|
@steverecio that doesn't sound right - the email addresses are being extracted and made available on @rogeriosmorais I just built it into my projects by overriding adapters. It's pretty straightforward that way - don't forget to write some tests to make sure behaviour is as you expect it! |
@sspross I'm a bit worried people are copy/pasting your code without being aware that it is insecure if they support email/password auth or some provider that does not properly validate email addresses. It might be worth updating/removing. Cheers! |
@lukeburden looks like this changed between 0.39.1 and 0.41.0. I'm now seeing the email addresses in the adapter however there are two issues with the out of the box GoogleProvider. The Google API returns The Google API returns the uid in I have an open PR for fixing the id vs sub issue but I'm not sure if this would break any old code / APIs. Here is the custom provider I built to make this work for Google:
|
@steverecio it sounds like this has changed on Google's side at some point, although I've found it hard to find any changelog explicitly stating that it has. Version 1 of their TokenInfo has both Hopefully your PR to change the Google Provider can be aware of the version on Google's side. Thanks for your efforts! |
Hello Gents, am stuck on this issue... been 3 days! I wonder how to proceed. Did you agree here on the way forward for pre_social_login? And, what GoogleProvider to use> |
Wow! Finally I got things working. I used @steverecio's solution where he suggests that with Google Login, the sociallogin object doesn't have any emailaddresses, therefore we are bound to source the email address from sociallogin.account.extra_data. |
It seems that @steverecio is using a custom adapter, that is feeding different info into the provider, which explains why he's seeing different properties. See #2443 for some more details. It might be that this different adapter is somehow relevant to this discussion (I'm not sure), though. Also, I just set up things with the default allauth google adapter and provider, and my e-mailaddress was connected to the newly created account as expected, so my alluath does have access to it (but maybe I'm missing the point here). |
Here is the custom adapter for transparency (running allauth==0.41.0)
|
@matthijskooijman Please just mention the error you are getting... It is important to focus laser like on the error in order to find out what code is throwing it. |
@richardbalwane, I'm not getting any errors, things are working as expected for me. I have not applied any of the modifications here to autoconnect an account to an existing e-mail, but I was just responding to earlier comments that the e-mailaddress would not be available. I might have misunderstood some of the discussion, so if my remarks seem confusing, better just ignore them :-) |
Eu fiz desse jeito e deu certo `from allauth.account.models import EmailAddress class SocialAdapter(DefaultSocialAccountAdapter):
|
After making this question in StackOverflow, I've just discovered this comment in the source code helpers.py file:
I want to argue that this would be a nice feature, and I'd like to offer myself to implement it if there is a reasonable chance that my patch will be accepted in the main trunk.
First, I agree that it is a security risk and that it should be turned off by default. There should be an configuration option to turn on the automatic connection.
Not all social providers are equal. Some of them, like Facebook, verify the user email and account. Facebook even sends a
"verified_email": true
that is saved in the extra_data column of socialaccount_socialaccount table. If the email if from Gmail, Google provider is who created the account! It can't be more verified than this.Automatically connecting, would be a great usability enhancement. Just one click authenticating. Less friction to use my site.
Automatically connecting the accounts is a reasonable security/usability tradeoff for some selected providers.
Ultimately, I believe the security tradeoffs should be made by the site owner. The user of the library decides which risks are acceptable to run, and if a improved user experience is more important than a remote probability security exploit.
Just using myself as an example. I plan to offer just Facebook and Google as login options. My site is a hobby site, there is nothing worth to hack their user accounts. I want just an easy way to manage the identity of my users. As I said before, both FB and Google check the users email. I really think that if someone wants to hack my users accounts, it would be easier to directly hack my site than to hack FB and to use to it hack my site. The better user experience would be a greater benefit.
Please give me a "go forward" signal before I start implementing it.
The text was updated successfully, but these errors were encountered: