Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem with do_complete for Facebook backend #39

Closed
nmundar opened this issue Sep 26, 2013 · 21 comments
Closed

Problem with do_complete for Facebook backend #39

nmundar opened this issue Sep 26, 2013 · 21 comments

Comments

@nmundar
Copy link

nmundar commented Sep 26, 2013

I'm using Django 1.5.4 and Django Social Auth 0.7.28 without a problem but wanted to make a switch because of deprecation. During connecting procedure with facebook i can never complete the procedure. I've tried going throught python-social-auth code to see what was going on but couldn't figure it out beside the django authenticate() returning None.

Connecting procedure:

click on:

127.0.0.1 - - [26/Sep/2013 11:33:52] "GET /login/facebook/ HTTP/1.1" 302 -
127.0.0.1 - - [26/Sep/2013 11:33:53] "GET /complete/facebook/?redirect_state=n4Bm...SeNJw&code=AQASofC...MRa57z3o&state=n4Bm...SeNJw HTTP/1.1" 302 -
127.0.0.1 - - [26/Sep/2013 11:33:53] "GET /accounts/login-error/ HTTP/1.1" 404 - (i dont have login-error currently implemented)

This is roughly the state of the various functions that python-social-auth calls

  1. do_auth facebook -> redirect /dialog/oauth
  2. complete facebook

do_complete

strategy.request_data()
data:  {u'state': u'n4Bm...SeNJw', u'code': u'AQASofC...MRa57z3o', u'redirect_state': u'n4Bm...SeNJw'}

partial is None

- FacebookOAuth2 auth_complete
    state: n4BmS...SeNJw
    key: 211331415633513
    url: https://graph.facebook.com/oauth/access_token
    access_token:  CAAD...yDE

- FacebookOAuth2 do_auth ---

    - FacebookOAuth2 user_data ---
        data:  {u'username': u'ho...c', ..., u'id': u'100...92'}

    - DjangoStrategy authenticate--
        args: ()
        kwargs contains complete request
        kwargs response: {
            u'username': u'ho...c', ..., # other profile data
            'access_token': u'CAAD...yDE',
            u'id': u'100004043024192'
        }
        kwargs user: None
        kwargs backend:  social.backends.facebook.FacebookOAuth2
        DjangoStrategy backend:  social.backends.facebook.FacebookOAuth2

        django.contrib.auth.authenticate returns  None

not authenticated, not user hence
url = login_error_url  # /accounts/login-error/

I've also documented all the changes from django-social-auth (OLD) to python-social-auth (NEW):

Django==1.5.4
django-social-auth==0.7.28
python-social-auth==0.1.13

/accounts/middleware.py

OLD:
from social_auth.middleware import SocialAuthExceptionMiddleware
from social_auth.exceptions import AuthCanceled

NEW:
from social.apps.django_app.middleware import SocialAuthExceptionMiddleware
from social.exceptions import AuthCanceled

accounts/templates/accounts/index.html

OLD
{% if not social_auth.facebook %}
a href="{% url 'socialauth_associate_begin' 'facebook' %}"
NEW
{% if not social.facebook %}
a href="{% url 'social:begin' 'facebook' %}"

accounts/templates/accounts/login.html

OLD:
href="{% url 'socialauth_begin' 'facebook' %}"

NEW:
href="{% url 'social:begin' 'facebook' %}"

cstorm/settings/base.py

OLD INSTALLED_APPS
'social_auth',

NEW INSTALLED_APPS
'social.apps.django_app.default',

OLD:
AUTHENTICATION_BACKENDS = (
'social_auth.backends.facebook.FacebookBackend',
'accounts.auth_backends.EmailOrUsernameBackend',
)

NEW:
AUTHENTICATION_BACKENDS = (
'social.backends.facebook.FacebookOAuth2',
'accounts.auth_backends.EmailOrUsernameBackend',
# 'django.contrib.auth.backends.ModelBackend', #doesnt make a difference
)

OLD:
LOGIN_REDIRECT_URL = '/accounts/postlogin/'
LOGIN_ERROR_URL = '/accounts/login-error/'

NEW:
SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/accounts/postlogin/'
SOCIAL_AUTH_LOGIN_ERROR_URL = '/accounts/login-error/'

OLD:
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.tz',
'django.core.context_processors.request',
'django.contrib.messages.context_processors.messages',
'social_auth.context_processors.social_auth_by_name_backends',
)

NEW:
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.tz',
'django.core.context_processors.request',
'django.contrib.messages.context_processors.messages',
'social.apps.django_app.context_processors.backends',
)

OLD:
SOCIAL_AUTH_PIPELINE = (
'social_auth.backends.pipeline.social.social_auth_user',
'social_auth.backends.pipeline.associate.associate_by_email',
'social_auth.backends.pipeline.user.get_username',
'social_auth.backends.pipeline.user.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',
'accounts.social_auth_pipeline.get_profile_data', # custom
'accounts.social_auth_pipeline.get_profile_avatar', # custom
)

NEW:
SOCIAL_AUTH_PIPELINE = (
'social.pipeline.social_auth.social_user',
'social.pipeline.social_auth.associate_by_email',
'social.pipeline.user.get_username',
'social.pipeline.user.create_user',
'social.pipeline.social_auth.associate_user',
'social.pipeline.social_auth.load_extra_data',
'social.pipeline.user.user_details',
'accounts.social_auth_pipeline.get_profile_data', # custom
'accounts.social_auth_pipeline.get_profile_avatar', # custom
)

OLD:
FACEBOOK_EXTENDED_PERMISSIONS = ['email', 'user_birthday']

NEW:
#FACEBOOK_EXTENDED_PERMISSIONS = ['email', 'user_birthday']
SOCIAL_AUTH_FACEBOOK_EXTENDED_PERMISSIONS = ['email', 'user_birthday']
SOCIAL_AUTH_FACEBOOK_SCOPE = ['email', 'user_birthday']

OLD:
FACEBOOK_APP_ID = '...'
FACEBOOK_API_SECRET = '...'

NEW:
SOCIAL_AUTH_FACEBOOK_KEY = '...'
SOCIAL_AUTH_FACEBOOK_SECRET = '...'

urls.py

OLD:
(r'^sso/', include('social_auth.urls')),

NEW:
(r'^sso/', include('social.apps.django_app.urls', namespace='social')),

@omab
Copy link
Owner

omab commented Sep 26, 2013

Add these entries to the top of you pipeline setting:

    'social.pipeline.social_auth.social_details',
    'social.pipeline.social_auth.social_uid',
    'social.pipeline.social_auth.auth_allowed',

So, it should look like:

SOCIAL_AUTH_PIPELINE = (
    'social.pipeline.social_auth.social_details',
    'social.pipeline.social_auth.social_uid',
    'social.pipeline.social_auth.auth_allowed',
    'social.pipeline.social_auth.social_user',
    'social.pipeline.social_auth.associate_by_email',
    'social.pipeline.user.get_username',
    'social.pipeline.user.create_user',
    'social.pipeline.social_auth.associate_user',
    'social.pipeline.social_auth.load_extra_data',
    'social.pipeline.user.user_details',
    'accounts.social_auth_pipeline.get_profile_data', # custom
    'accounts.social_auth_pipeline.get_profile_avatar', # custom
)

@nmundar
Copy link
Author

nmundar commented Sep 27, 2013

Thanks for response.

I've tried adding those settings to the pipeline setting. In do_complete i have AnonymousUser as user after strategy.session_get but in authenticate method of strategy kwargs['user'] is still None.

@omab
Copy link
Owner

omab commented Sep 27, 2013

@nmundar, user will be an AnonynousUser before this line https://github.com/omab/python-social-auth/blob/master/social/actions.py#L56 (actually a little above, before any user = ... line). And the strategy.authenticate will always have kwargs['user'] equal to None unless this is an association with an already created account. Does your custom pipeline entries return anything? Does the process work if you comment those two?

@nmundar
Copy link
Author

nmundar commented Sep 30, 2013

I see no change when i remove custom pipelines, this is how they are implemented

from datetime import datetime
from uuid import uuid4
from urllib2 import urlopen, HTTPError
from django.contrib.auth.models import User
from django.template.defaultfilters import slugify
from django.core.files.base import ContentFile
from models import UserProfile


def get_profile_data(backend, details, response, social_user,
                     uid, user, *args, **kwargs):
    profile, new_user = UserProfile.objects.get_or_create(user=user)

    if backend.__class__.__name__ == 'FacebookOAuth2':   # changed from FacebookBackend, no effect

        if not user.email and response.get('email'):
            user.email = response.get('email')

        if not profile.gender and response.get('gender'):
            profile.gender = response.get('gender')

        if not profile.birthday and response.get('birthday'):
            datestring = response.get('birthday')
            date_format = "%m/%d/%Y"
            profile.birthday = datetime.strptime(datestring, date_format)

        profile.save()
        user.save()


def get_profile_avatar(backend, details, response, social_user,
                       uid, user, *args, **kwargs):
    url = None
    profile = user.get_profile()
    if not profile.profile_photo:
        if backend.__class__.__name__ == 'FacebookOAuth2':
            url = "http://graph.facebook.com/%s/picture?type=large" % \
                  response.get('id')

        if url:
            try:
                avatar = urlopen(url)
                rstring = uuid4().get_hex()
                profile.profile_photo.save(slugify(rstring + '_p') + '.jpg',
                                           ContentFile(avatar.read()))
                profile.save()
            except HTTPError:
                pass

@omab
Copy link
Owner

omab commented Oct 3, 2013

@nmundar, any luck with this? I don't see any problem with your settings or pipeline methods.

@jasonwaters
Copy link

I'm having this exact same problem after migrating from django-social-auth to python-social-auth. do_complete never gets a valid user. It's always None. and partial = partial_pipeline_data(strategy, user, _args, *_kwargs) also returns None. So it always redirects to the login_error page.

I'm not using facbook, but my own backend for Runkeeper. worked like a champ in django-social-auth.

Do you mind looking at my diff to see if you can see the problem?

jasonwaters/fitcompetition@05a0014

@taivo
Copy link

taivo commented Nov 1, 2013

I also switched from django-social-auth to python-social-auth and have a problem with very similar symptoms. I was able to track down my problem. Hopefully, this summary will help you guys:

Causes:

  1. I had an old setting from django-social-auth: SOCIAL_AUTH_EXTRA_DATA = False
  2. In the extra_data step of the pipeline, the backends.oauth.extra_data function does this concatenation
    names = (self.EXTRA_DATA or []) + \
    self.setting('EXTRA_DATA', [])

So an exception was thrown because a list is concatenated with a bool and this exception propagated up the stack and eventually invalidated the entire authentication pipeline.

Solution: removing the setting SOCIAL_AUTH_EXTRA_DATA.

Suggestion: perhaps some note on SOCIAL_AUTH_EXTRA_DATA 's new usage (as list) or its deprecation should be mentioned in the "Porting settings" docs. Also, it looks like backends.oauth.extra_data is retrieving settings that are list of strings, perhaps we can raise an explicit exception here if EXTRA_DATA is of a different type.

@omab, I can submit a PR but I'm hoping to find out from you first about the role of SOCIAL_AUTH_EXTRA_DATA in this new codebase. Thanks.

@jasonwaters
Copy link

Thanks for the comment. Sadly it's not the problem for me. I don't have the SOCIAL_AUTH_EXTRA_DATA setting at all, and it doesn't look like python-social-auth is even getting into the pipeline at all.

@omab
Copy link
Owner

omab commented Nov 2, 2013

So far I wasn't able to reproduce this issue. @jasonwaters could you set a try-except block around these lines https://github.com/omab/python-social-auth/blob/master/social/backends/base.py#L72-L83 and check if there's any exception raised there?

@jasonwaters
Copy link

There was an exception raised:

'social_details() takes at least 2 arguments (2 given)'

see https://www.dropbox.com/s/ci6p5v8255xdscn/Screenshot%202013-11-02%2011.29.47.png

@omab
Copy link
Owner

omab commented Nov 2, 2013

Could you determine the two arguments passed?

@jasonwaters
Copy link

There are no arguments passed into social_details(). I traced it to run_pipeline where it calls the method by reflection:

result = func(_args, *_out)

*args is an empty tuple.

https://www.dropbox.com/s/1bxh1g8cyy917sz/Screenshot%202013-11-02%2016.14.24.png

It looks like args are passed down all the way from the response where they originate as an empty tuple. I'm puzzled at this point, since I don't have a clear understanding of how it should work.

@jasonwaters
Copy link

I'm pretty much dead in the water on this. Everything was working fine with django-social-auth, but after the switch to python-social-auth I'm stuck. I want to adopt the new library, but it just doesn't work. Anything else I can do to get you information to troubleshoot this issue? My entire project is on github.

@omab
Copy link
Owner

omab commented Nov 4, 2013

@jasonwaters, from the last screenshot social_details() is getting arguments from **kwargs. How did you install python-social-auth, pip or cloning the repository?

@omab
Copy link
Owner

omab commented Nov 4, 2013

@jasonwaters, I was able to reproduce your case and I'm working on a fix

@jasonwaters
Copy link

Great news! Thanks. I installed with pip, BTW.

@omab
Copy link
Owner

omab commented Nov 4, 2013

@jasonwaters, I've released v0.1.15, could you give it a try?

@jasonwaters
Copy link

It's working now. Thank's a lot! What was the problem?

@omab
Copy link
Owner

omab commented Nov 13, 2013

Wrong handling of arguments passed to the pipeline.

@omab omab closed this as completed Nov 13, 2013
@nmundar
Copy link
Author

nmundar commented Dec 16, 2013

I managed to catch some time to try to make this work. I've succeded. The problem was actualy in my pipeline due to extra social_user param. I've managed to find it because I've been messing inside django authenticate method which silences exceptions that are due to incorrect number of arguments.

@aliscie
Copy link

aliscie commented Mar 18, 2021

I managed to catch some time to try to make this work. I've succeded. The problem was actualy in my pipeline due to extra social_user param. I've managed to find it because I've been messing inside django authenticate method which silences exceptions that are due to incorrect number of arguments.

What exactly you did ? I don't really understand

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants