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

AttributeError: 'generator' object has no attribute 'session' #10

Closed
mikeedwards opened this issue Aug 8, 2015 · 8 comments
Closed
Labels

Comments

@mikeedwards
Copy link

Ok, this is a really weird error and it passes through a few modules.

When I try to POST to auth/convert-token, like so:

curl -X POST -d "grant_type=convert_token&client_id=<client_id>&client_secret=<client_secret>&backend=facebook&token=<token>" http://localhost:8000/auth/convert-token

I get the error:
AttributeError: 'generator' object has no attribute 'session'

I notice that ConvertTokenView calls the OAuthLibMixin for create_token_response:

(see rest_framework_social_oauth2/views.py line 34)

class ConvertTokenView(CsrfExemptMixin, OAuthLibMixin, View):
    """
    Implements an endpoint to provide access tokens
    The endpoint is used in the following flows:
    * Authorization code
    * Password
    * Client credentials
    """
    server_class = SocialTokenServer
    validator_class = oauth2_settings.OAUTH2_VALIDATOR_CLASS
    oauthlib_backend_class = KeepRequestCore

    def post(self, request, *args, **kwargs):
        url, headers, body, status = self.create_token_response(request)
        response = HttpResponse(content=body, status=status)

        for k, v in headers.items():
            response[k] = v
        return response

This leads down a winding path that eventually gets to oauthlib. In the code below, the header value for 'Django-request-object' goes from being a WSGIRequest to a generator without any output, the result of the encode method here:

(see oauthlib/common.py line 380)
self.headers = CaseInsensitiveDict(encode(headers or {}))

The problem, then, is that by the time the request gets back to the SocialTokenGrant class in your library here:

(see rest_framework_social_oauth2/oauth2_grants.py line 80)

        # TODO: Find a better way to pass the django request object
        strategy = load_strategy(request=request.headers["Django-request-object"])

It's no longer a real request. And, a few calls down the chain in python-social-auth, it blows up:

(see social/strategies/django_strategy.py line 30

class DjangoStrategy(BaseStrategy):
    DEFAULT_TEMPLATE_STRATEGY = DjangoTemplateStrategy

    def __init__(self, storage, request=None, tpl=None):
        self.request = request
        self.session = request.session if request else {}
        super(DjangoStrategy, self).__init__(storage, tpl)

and produces the error:
AttributeError: 'generator' object has no attribute 'session'

Any idea how to work around this? Is there another way to get back to the Django request without having to rely on the headers coming through?

Here are the relevant parts of my requirements.txt:

Django==1.8.1
djangorestframework==3.1.2
six==1.9.0
wsgiref==0.1.2
python-social-auth==0.2.12
django-oauth-toolkit==0.9.0
django-rest-framework-social-oauth2==1.0.0

and settings.py

AUTHENTICATION_BACKENDS = (
    # Facebook OAuth2
    'social.backends.facebook.FacebookAppOAuth2',
    'social.backends.facebook.FacebookOAuth2',

    # django-rest-framework-social-oauth2
    'rest_framework_social_oauth2.backends.DjangoOAuth2',

    # Django
    'django.contrib.auth.backends.ModelBackend',
)


REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
        'oauth2_provider.ext.rest_framework.OAuth2Authentication',
        'rest_framework_social_oauth2.authentication.SocialAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
}

SOCIAL_AUTH_FACEBOOK_KEY = env_var('SOCIAL_AUTH_FACEBOOK_KEY')
SOCIAL_AUTH_FACEBOOK_SECRET = env_var('SOCIAL_AUTH_FACEBOOK_SECRET')

TEMPLATE_CONTEXT_PROCESSORS = (
    'django.contrib.auth.context_processors.auth',
    'django.template.context_processors.debug',
    'django.template.context_processors.i18n',
    'django.template.context_processors.media',
    'django.template.context_processors.static',
    'django.template.context_processors.tz',
    'django.contrib.messages.context_processors.messages',
    'social.apps.django_app.context_processors.backends',
    'social.apps.django_app.context_processors.login_redirect',
)

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'social.apps.django_app.middleware.SocialAuthExceptionMiddleware',
)
@mikeedwards
Copy link
Author

FWIW it seems like when I change:

        strategy = load_strategy(request=request.headers["Django-request-object"])

to

        strategy = load_strategy()

nothing bad seems to happen and I get my token. Does the strategy definitely need to get the original Django request?

@PhilipGarnero
Copy link
Collaborator

The request is in fact not required but when not used, the strategy used by python social auth looses informations that could be crucial later (in a pipeline for example).
I will try to find another way to fix this.
By the way, which python version are you using ?

@mikeedwards
Copy link
Author

Ah, sorry, forgot to mention that. Python 2.7.6

@PhilipGarnero
Copy link
Collaborator

I couldn't reproduce your bug.
Could you tell me the version of oauthlib you are using ?

@mikeedwards
Copy link
Author

Sure, it's oauthlib==1.0.1.

The bug does seems to be related to what oauthlib is doing in common.py on line 380, but I'm not sure why we'd get different results--looking at the commit history for that project, that line has been there for a long time.

@PhilipGarnero
Copy link
Collaborator

Well, I still couldn't reproduce but I tried something anyway.
Could you use the branch v1.0.2 to see if that fixes it for you ?

@felix-d
Copy link

felix-d commented Aug 12, 2015

I reproduced it trying to POST to auth/convert_token as well, using python 3.4 and oauthlib 1.0.1.

defusedxml==0.4.1
Django==1.8.3
django-braces==1.8.1
django-filter==0.10.0
django-oauth-toolkit==0.9.0
django-rest-framework-social-oauth2==1.0.0
djangorestframework==3.1.3
Markdown==2.6.2
oauthlib==1.0.1
PyJWT==1.4.0
python-social-auth==0.2.12
python3-openid==3.0.5
requests==2.7.0
requests-oauthlib==0.5.0
six==1.9.0

@felix-d
Copy link

felix-d commented Aug 12, 2015

create_token_response override in v1.0.2 fixes it for me!

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

No branches or pull requests

3 participants