Django-social auth KeyError
I get a KeyError in my partial pipeline when I try to sign up for Twitter accounts while facebook accounts are working fine. This is weird because the same function works great with facebook users.
The error message looks like this:
KeyError at / myapp / 'Partial_pipeline'
in 'myapp_auth_form' and my code is:
settings.py
SOCIAL_AUTH_ENABLED_BACKENDS=('facebook','twitter',)
SOCIAL_AUTH_DEFAULT_USERNAME='new_social_auth_user'
...
TWITTER_CONSUMER_KEY = 'mytwitterconsumerkey'
TWITTER_CONSUMER_SECRET = 'mytwitterconsumersecret'
LOGIN_URL = '/login/'
LOGIN_REDIRECT_URL = '/'
LOGIN_ERROR_URL = '/login-error/'
SOCIAL_AUTH_PIPELINE = (
'social_auth.backends.pipeline.social.social_auth_user',
'social_auth.backends.pipeline.misc.save_status_to_session',
'myapp.pipeline.has_email',
'myapp.pipeline.check_by_email',
'myapp.pipeline.redirect_to_form',
'myapp.pipeline.get_username',
'myapp.pipeline.create_user',
'social_auth.backends.pipeline.social.associate_user',
'social_auth.backends.pipeline.social.load_extra_data',
'social_auth.backends.pipeline.user.update_user_details'
)
myapp.pipeline
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from social_auth.models import UserSocialAuth
from registration.models import UserProfile
def has_email(details, user=None, *args, **kwargs):
"""Check if email is provided and ask for it otherwise
"""
if user:
return None
email = details.get('email')
if email:
kwargs['request'].session['saved_email'] = email
else:
session = kwargs['request'].session
email = session.get('saved_email')
if not email:
return HttpResponseRedirect(reverse('myapp_email_form'))
def check_by_email(details, user=None, user_exists=UserSocialAuth.simple_user_exists, *args, **kwargs):
"""Check if there user with same email address and ask for its password to associate
"""
if user:
return None
session = kwargs['request'].session
email = session.get('saved_email')
if email:
if user_exists(username=email):
return HttpResponseRedirect(reverse('myapp_auth_form'))
def redirect_to_form(*args, **kwargs):
"""Redirect to get password if user is None
"""
session = kwargs['request'].session
if not session.get('saved_password') and not session.get('saved_nickname') and not session.get('saved_sex') and kwargs.get('user') is None:
return HttpResponseRedirect(reverse('social_auth_form'))
def get_username(details, user=None, *args, **kwargs):
"""Return an username for new user. Return current user username
if user was given.
Returns email address since myapp uses email for username
"""
if user:
return {'username': user.username}
username = details.get('email') or ''
return {'username': username}
def create_user(backend, details, response, uid, username, user=None, *args, **kwargs):
"""Create user and user profile. Depends on get_username pipeline."""
if user:
return {'user': user}
if not username:
return None
request = kwargs['request']
password = request.session.get('saved_password') or ''
user = UserSocialAuth.create_user(username=username, email=username, password=password)
nickname = request.session.get('saved_nickname') or ''
sex = request.session.get('saved_sex') or 'F'
profile = UserProfile.objects.create(user = user, nickname = nickname, sex = sex)
referee_nickname = request.session.get('saved_referee') or False
# if there was a recommender
if referee_nickname:
try:
referee_profile = UserProfile.objects.get(nickname=referee_nickname)
profile.referee = referee_profile.user
profile.save()
except UserProfile.DoesNotExist:
pass
return {
'user': user,
'is_new': True
}
views.py
from social_auth.utils import setting
from django.contrib.auth import authenticate, login
def myapp_email_form(request):
# if user is logged in already, redirect the user to home
if request.user.is_authenticated():
if request.GET.get('mobile', False):
return HttpResponseRedirect(reverse('m_home'))
return HttpResponseRedirect(reverse('home'))
""" If email is unprovided, ask for it
"""
if request.method == 'POST':
form = EmailForm(request.POST)
if form.is_valid():
email = form.cleaned_data['email']
name = setting('SOCIAL_AUTH_PARTIAL_PIPELINE_KEY', 'partial_pipeline')
request.session['saved_email'] = email
backend = request.session[name]['backend']
return redirect('socialauth_complete', backend=backend)
else:
form = EmailForm()
email = request.session.get('saved_email') or ''
variables = RequestContext(request, {
'form': form,
'email': email,
})
if request.is_mobile or request.GET.get('mobile', False):
return render_to_response('mobile/registration/social/email_form.html', variables, context_instance=RequestContext(request))
return render_to_response('.../email_form.html', variables, context_instance=RequestContext(request))
def myapp_auth_form(request):
# if user is logged in already, redirect the user to home
if request.user.is_authenticated():
if request.GET.get('mobile', False):
return HttpResponseRedirect(reverse('m_home'))
return HttpResponseRedirect(reverse('home'))
""" If user email is already registered to myapp, ask user for its password
"""
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
email = form.cleaned_data['username']
user = authenticate(username=email, password=form.cleaned_data['password'])
if user is not None:
if user.is_active:
login(request, user)
name = setting('SOCIAL_AUTH_PARTIAL_PIPELINE_KEY', 'partial_pipeline')
request.session['saved_user'] = user
############################################
backend = request.session[name]['backend'] #<- I'm getting the KeyError at this point
############################################
return redirect('socialauth_complete', backend=backend)
else:
return HttpResponseRedirect(reverse('inactive_user'))
else:
form.non_field_errors = _('A user with such email and password does not exist.')
else:
form = LoginForm()
email = request.session.get('saved_email') or ''
variables = RequestContext(request, {
'form': form,
'email': email,
})
if request.is_mobile or request.GET.get('mobile', False):
return render_to_response('mobile/registration/social/auth_form.html', variables, context_instance=RequestContext(request))
return render_to_response('.../auth_form.html', variables, context_instance=RequestContext(request))
def social_auth_form(request):
# if user is logged in already, redirect the user to home
if request.user.is_authenticated():
if request.GET.get('mobile', False):
return HttpResponseRedirect(reverse('m_home'))
return HttpResponseRedirect(reverse('home'))
""" Remedy form taking missing information during social authentication
"""
nickname = ''
sex = 'F'
if request.method == 'POST':
form = SocialRegistrationForm(request.POST)
if form.is_valid():
nickname = form.cleaned_data['nickname']
sex = form.cleaned_data['sex']
name = setting('SOCIAL_AUTH_PARTIAL_PIPELINE_KEY', 'partial_pipeline')
request.session['saved_nickname'] = nickname
request.session['saved_sex'] = sex
request.session['saved_password'] = form.cleaned_data['password1']
backend = request.session[name]['backend']
return redirect('socialauth_complete', backend=backend)
else:
form = SocialRegistrationForm()
nickname = request.session.get('saved_username') or ''
sex = request.session.get('saved_gender') or 'F'
if sex == 'male':
sex = 'M'
elif sex == 'female':
sex = 'F'
variables = RequestContext(request, {
'form': form,
'nickname': nickname,
'sex': sex,
})
if request.is_mobile or request.GET.get('mobile', False):
return render_to_response('mobile/registration/social/social_form.html', variables, context_instance=RequestContext(request))
return render_to_response('.../auth_form.html', variables, context_instance=RequestContext(request))
source to share
You need to add 'social_auth.backends.pipeline.misc.save_status_to_session'
before every method that calls the redirect and stops the process. It works with Facebook because Facebook exposes email addresses, but Twitter doesn't. So, add this method before any entry that does the redirect, or call it inside your pipeline code before doing the redirection.
(Just post a comment as an answer so you can select it)
source to share
You are getting the following error because you are trying to access the session name using this request.session[name]
. This format is supposed to be used when saving the session. To fix this,
name = setting('SOCIAL_AUTH_PARTIAL_PIPELINE_KEY', 'partial_pipeline')
request.session['saved_user'] = user
############################################
request.session['name'] = name
backend = request.session.get('name') // this is the session format in getting the data
############################################
return redirect('socialauth_complete', backend=backend)
source to share