I'm building an API using Django Rest Framework. Later this API is supposed to be consumed by iOS and Android devices. I want to allow my users to sign-up with oauth2-providers like Facebook and Google. In this case, they shouldn't have to create an account with my platform at all. But users should also be able to sign-up when not having a Facebook/Google account, for which I'm using django-oauth-toolkit, so I have my own oauth2-provider.
For external providers I'm using python-social-auth, which works fine and automatically creates the user objects.
I want the clients to authenticate by using bearer tokens, which works fine for users that signed up with my provider (django-oauth-toolkit provides authentication scheme and permission classes for Django REST Framework).
However, python-social-auth only implements session based authentication, so there is no straightforward way to make authenticated API requests on behalf of users that registered by an external oauth2 provider.
If I use an access_token that has been generated by django-oauth-toolkit, doing a request like this works:
curl -v -H "Authorization: Bearer <token_generated_by_django-oauth-toolkit>" http://localhost:8000/api/
However, the following doesn't work since there is no corresponding authentication scheme for Django REST Framework and the AUTHENTICATION_BACKENDS provided by python-social-auth only work for session-based authentication:
curl -v -H "Authorization: Bearer <token_stored_by_python-social-auth>" http://localhost:8000/api/
Using the browseable API provided by Django REST Framework after authenticating with python-social-auth works just fine, only API calls without a session cookie don't work.
I'm wondering what the best approach is for this problem. The way I see it, I have basically two options:
A: When a user signs up with an external oauth2 provider (handled by python-social-auth), hook into the process to create an oauth2_provider.models.AccessToken and continue to use 'oauth2_provider.ext.rest_framework.OAuth2Authentication'
, now authenticating also users that registered with an external provider. This approach is suggested here:
https://groups.google.com/d/msg/django-rest-framework/ACKx1kY7kZM/YPWFA2DP9LwJ
B: Use python-social-auth for API request authentication. I could get my own users into python-social-auth by writing a custom backend and using register_by_access_token. However, since API calls cannot utilize Django sessions this would mean I would have to write an authentication scheme for Django Rest Framework that utilizes the data stored by python-social-auth. Some pointers on how to do this can be found here:
http://psa.matiasaguirre.net/docs/use_cases.html#signup-by-oauth-access-token
http://blog.wizer.fr/2013/11/angularjs-facebook-with-a-django-rest-api/
http://cbdev.blogspot.it/2014/02/facebook-login-with-angularjs-django.html
However, the way I understand it python-social-auth only verifies the token when doing a login and relies on the Django session afterwards. This would mean I would have to find a way to prevent python-social-auth from doing the whole oauth2-flow for each stateless API request and rather check against the data stored in the DB, which isn't really optimized for querying since it's stored as JSON (I could use UserSocialAuth.objects.get(extra_data__contains=) though).
I would also have to take care of verifying the scopes of an access token and use them to check permissions, something django-oauth-toolkit already does (TokenHasScope
, required_scopes
etc).
At the moment, I'm leaning towards using option A, since django-oauth-toolkit provides good integration with Django Rest Framework and I get everything I need out of the box. The only drawback is that I have to "inject" the access_tokens retrieved by python-social-auth into the AccessToken model of django-oauth-toolkit, which feels wrong somehow, but would probably be by far the easiest approach.
Does anybody have any objections on doing that or has maybe tackled the same problem in a different way? Am I missing something obvious and making my life harder than necessary? If anybody has already integrated django-oauth-toolkit with python-social-auth and external oauth2 providers I would be very thankful for some pointers or opinions.