Django allauth social login: automatically linking social site profiles using the registered email

前端 未结 5 1103
鱼传尺愫
鱼传尺愫 2020-12-07 09:25

I aim to create the easiest login experience possible for the users of my Django site. I imagine something like:

  1. Login screen is presented to
5条回答
  •  一生所求
    2020-12-07 09:47

    As per babus comment on this related thread, the proposed answers posted before this one (1, 2) introduce a big security hole, documented in allauth docs:

    "It is not clear from the Facebook documentation whether or not the fact that the account is verified implies that the e-mail address is verified as well. For example, verification could also be done by phone or credit card. To be on the safe side, the default is to treat e-mail addresses from Facebook as unverified."

    Saying so, I can signup in facebook with your email ID or change my email to yours in facebook and login to the website to get access to your account.

    So taking this into consideration, and building on @sspross answer, my approach is to redirect the user to the login page, and notify her/him of the duplicate, and inviting him to log in with her/his other account, and link them once they are logged in. I acknowledge that differs from the original question, but in doing so, no security hole is introduced.

    Thus, my adapter looks like:

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

提交回复
热议问题