Search code examples
pythondjangodjango-viewsdjango-userspython-social-auth

python-social-auth: Unable to keep logged in newly registered user after setting password


I am certain the problem derives from this statement: user.set_password(local_password) because when I omit it the pipeline ends with the user logged in as expected.

Now, as others have pointed out, after using the set_password method django automatically finishes the session so to avoid that we may use update_session_auth_hash (request, user). The problem is that this does not work in the pipeline. I've also tried adding instead:

        user = authenticate(username=user, password=local_password)
        login(request, user)

This also does not work.

I also checked via print statements whether the user is authenticated, it happens to be in all 3 steps that I checked.

Lastly, I also tried creating a new pipeline method and calling it afterwards. This one also did not work.

@partial
def login_users(strategy, request, user, *args, **kwargs):
    user = authenticate(username=user, password=strategy.session_get('local_password', None))
    print(user.is_authenticated)
    request = strategy.request
    login(request, user)
    messages.success(request, "Welcome, you have successfully signed up")
    return

In summary, and to avoid overflowing with data here, everything works as expected, but as soon as I save the password via user.set_password(local_password), the user is logged out and needs to click again on Linkedin to sign in. Otherwise, the behavior would be as expected, i.e. the data collected is saved and the home page is shown at the end of the pipeline. Please see below for my pipeline.

@partial
def collect_password(strategy, request, details, is_new=False, *args, **kwargs):
    # session 'local_password' is set by the pipeline infrastructure
    # because it exists in FIELDS_STORED_IN_SESSION
    local_password = strategy.session_get('local_password', None)
    local_country = strategy.session_get('local_country', None)       
    if is_new:
        if not local_password:
            # if we return something besides a dict or None, then that is
            # returned to the user -- in this case we will redirect to a
            # view that can be used to get a password
            return redirect('social_signup')
            # grab the user object from the database (remember that they may
            # not be logged in yet) and set their password. (Assumes that the
            # email address was captured in an earlier step.)
        user = User.objects.get(email=details['email'])
        user.country = local_country
        user.set_password(local_password)
        user.save()
        update_session_auth_hash (request, user)
        print(user.is_authenticated)
        user = authenticate(username=user, password=local_password)
        login(request, user)
        print(user.is_authenticated)
    return 

I owe you a coffee if you happen to know this one :D. Thanks so much in advance.


Solution

  • The partial pipeline is a stop along the auth process. This password setting code might be better with extending the backend and overriding the get_user_details method.

    That said, the partial pipeline is for doing some work before the actual auth occurs. Calling authenticate and return are not needed, social-auth will do this after the partial code completes.

    Also, the current user instance can be accessed as a parameter.

    @partial
    def collect_password(strategy, backend, request, details, user=None, is_new=False, *args, **kwargs):
        # session 'local_password' is set by the pipeline infrastructure
        # because it exists in FIELDS_STORED_IN_SESSION
        local_password = strategy.session_get('local_password', None)
        local_country = strategy.session_get('local_country', None)
    
        if is_new:
            if not local_password:
                return strategy.redirect('social_signup') # changed redirect to use p-s-a methods
    
            if user: # user is a parameter to the pipeline methods
                user.country = local_country
                user.set_password(local_password)
                user.save()