Search code examples
pythondjangopython-social-auth

Missing needed parameter state Python social auth Email Validation


I am using python social auth with django app. In email validation, the link received on the mail works fine in the same browser from which authentication was initiated but it show Missing needed parameter state in different browser.Did anybody fix this issue ?

The issue is discussed here Issue #577


Solution

  • This is because there's no partial pipeline data in other browser!

    Christopher Keefer has worked on monkey-patch for this by fetching the session_key for Django session table. There's also a blog article here on this , refer Step 3 of this article.

    # Monkey patching - an occasionally necessary evil.
    from social import utils
    from social.exceptions import InvalidEmail
    
    from django.core import signing
    from django.core.signing import BadSignature
    from django.contrib.sessions.models import Session
    from django.conf import settings
    
    
    def partial_pipeline_data(backend, user=None, *args, **kwargs):
        """
        Monkey-patch utils.partial_pipeline_data to enable us to retrieve session data by signature key in request.
        This is necessary to allow users to follow a link in an email to validate their account from a different
        browser than the one they were using to sign up for the account, or after they've closed/re-opened said
        browser and potentially flushed their cookies. By adding the session key to a signed base64 encoded signature
        on the email request, we can retrieve the necessary details from our Django session table.
        We fetch only the needed details to complete the pipeline authorization process from the session, to prevent
        nefarious use.
        """
        data = backend.strategy.request_data()
        if 'signature' in data:
            try:
                signed_details = signing.loads(data['signature'], key=settings.EMAIL_SECRET_KEY)
                session = Session.objects.get(pk=signed_details['session_key'])
            except BadSignature, Session.DoesNotExist:
                raise InvalidEmail(backend)
    
            session_details = session.get_decoded()
            backend.strategy.session_set('email_validation_address', session_details['email_validation_address'])
            backend.strategy.session_set('next', session_details.get('next'))
            backend.strategy.session_set('partial_pipeline', session_details['partial_pipeline'])
            backend.strategy.session_set(backend.name + '_state', session_details.get(backend.name + '_state'))
            backend.strategy.session_set(backend.name + 'unauthorized_token_name',
                                         session_details.get(backend.name + 'unauthorized_token_name'))
    
        partial = backend.strategy.session_get('partial_pipeline', None)
        if partial:
            idx, backend_name, xargs, xkwargs = \
                backend.strategy.partial_from_session(partial)
            if backend_name == backend.name:
                kwargs.setdefault('pipeline_index', idx)
                if user:  # don't update user if it's None
                    kwargs.setdefault('user', user)
                kwargs.setdefault('request', backend.strategy.request_data())
                xkwargs.update(kwargs)
                return xargs, xkwargs
            else:
                backend.strategy.clean_partial_pipeline()
    utils.partial_pipeline_data = partial_pipeline_data
    

    This fixes the problem to much extent, still its not perfect. It will fail if session_key gets deleted/changed in the database. Django updates session_key each time the session data changes. So in case any other user logs in the same browser the session_key gets changed and user can't verify with the email link.

    Omab has mentioned in the comment on issue,

    I see the problem now, and even if I think that this could be solved with a re-write of the email validation pipeline, this affects all the pipeline functions that use the partial mechanism, so, I'm already working on a restructure of the pipeline serialization functionality that will improve this behavior. Basically the pipeline data will be dumped to a DB table and a hash code will be used to identify the processes which can be stopped and continue later, removing the dependency of the session.

    Looking for update on this.