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
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.