Django Rest Framework authentication with OAuth2 [duplicate] - django

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.

A lot of the difficulty in implementing OAuth comes down to understanding how the authorization flow is supposed to work. This is mostly because this is the "starting point" for logging in, and when working with a third-party backend (using something like Python Social Auth) you are actually doing this twice: once for your API and once for the third-party API.
Authorizing requests using your API and a third-party backend
The authentication process that you need is go through is:
Mobile App -> Your API : Authorization redirect
Your API -> Django Login : Displays login page
Django Login -> Facebook : User signs in
Facebook -> Django Login : User authorizes your API
Django Login -> Your API : User signs in
Your API -> Mobile App : User authorizes mobile app
I'm using "Facebook" as the third-party backend here, but the process is the same for any backend.
From the perspective of your mobile app, you are only redirecting to the /authorize url provided by Django OAuth Toolkit. From there, the mobile app waits until the callback url is reached, just like in the standard OAuth authorization flow. Almost everything else (Django login, social login, etc.) is handled by either Django OAuth Toolkit or Python Social Auth in the background.
This will also be compatible with pretty much any OAuth libraries that you use, and the authorization flow will work the same no matter what third party backend is used. It will even handle the (common) case where you need to be able to support Django's authentication backend (email/username and password) as well as a third-party login.
Mobile App -> Your API : Authorization redirect
Your API -> Django Login : Displays login page
Django Login -> Your API : User signs in
Your API -> Mobile App : User authorizes mobile app
What's also important to note here is that the mobile app (which could be any OAuth client) never receives the Facebook/third-party OAuth tokens. This is incredibly important, as it makes sure your API acts as an intermediary between the OAuth client and you user's social accounts.
Mobile App -> Your API : Authorization redirect
Your API -> Mobile App : Receives OAuth token
Mobile App -> Your API : Requests the display name
Your API -> Facebook : Requests the full name
Facebook -> Your API : Sends back the full name
Your API -> Mobile App : Send back a display name
Otherwise, the OAuth client would be able to bypass your API and make requests on your behalf to the third-party APIs.
Mobile App -> Your API : Authorization redirect
Your API -> Mobile App : Receives Facebook token
Mobile App -> Facebook : Requests all of the followers
Facebook -> Mobile App : Sends any requested data
You'll notice that at this point you would have lost all control over the third-party tokens. This is especially dangerous because most tokens can access a wide range of data, which opens the door to abuse and eventually goes down under your name. Most likely, those logging into your API/website did not intend on sharing their social information with the OAuth client, and were instead expecting you to keep that information private (as much as possible), but instead you are exposing that information to everyone.
Authenticating requests to your API
When the mobile application then uses your OAuth token to make requests to your API, all of the authentication happens through Django OAuth Toolkit (or your OAuth provider) in the background. All you see is that there is a User associated with your request.
Mobile App -> Your API : Sends request with OAuth token
Your API -> Django OAuth Toolkit : Verifies the token
Django OAuth Toolkit -> Your API : Returns the user who is authenticated
Your API -> Mobile App : Sends requested data back
This is important, because after the authorization stage it shouldn't make a difference if the user is coming from Facebook or Django's authentication system. Your API just needs a User to work with, and your OAuth provider should be able to handle the authentication and verification of the token.
This isn't much different from how Django REST framework authenticates the user when using session-backed authentication.
Web Browser -> Your API : Sends session cookie
Your API -> Django : Verifies session token
Django -> Your API : Returns session data
Your API -> Django : Verifies the user session
Django -> Your API : Returns the logged in user
Your API -> Web Browser : Returns the requested data
Again, all of this is handled by Django OAuth Toolkit and does not require extra work to implement.
Working with a native SDK
In most cases, you are going to be authenticating the user through your own website and using Python Social Auth to handle everything. But the one notable exception is when using a native SDK, as authentication and authorization is handled through the native system, which means you are bypassing your API entirely. This is great for applications which need to sign in with a third party, or applications which don't use your API at all, but it's a nightmare when both come together.
This is because your server can't validate the login and is forced to assume that the login is valid and genuine, which means it bypasses any and all security that Python Social Auth gives you.
Mobile App -> Facebook SDK : Opens the authorization prompt
Facebook SDK -> Mobile App : Gets the Facebook token
Mobile App -> Your API : Sends the Facebook token for authorization
Your API -> Django Login : Tries to validate the token
Django Login -> Your API : Returns a matching user
Your API -> Mobile App : Sends back an OAuth token for the user
You'll notice that this skips over your API during the authentication phase, and then forces your API to make assumptions about the token that is passed in. But there are definitely cases where this risk may be worth it, so you should evaluate that before throwing it out. It's a trade off between quick and native logins for your user and potentially handling bad or malicious tokens.

I solved it by using your A. option.
What I do is registering users that use a third party to sign up by their third party access token.
url(r'^register-by-token/(?P<backend>[^/]+)/$',
views.register_by_access_token),
This way, I can issue a GET request like this one:
GET http://localhost:8000/register-by-token/facebook/?access_token=123456
And register_by_access_token gets called. request.backend.do_auth will query the provider for the user info from the token and magically register a user account with the info or sign in the user if he's already registered.
Then, I create a token manually and return it as JSON for letting the client query my API.
from oauthlib.common import generate_token
...
#psa('social:complete')
def register_by_access_token(request, backend):
# This view expects an access_token GET parameter, if it's needed,
# request.backend and request.strategy will be loaded with the current
# backend and strategy.
third_party_token = request.GET.get('access_token')
user = request.backend.do_auth(third_party_token)
if user:
login(request, user)
# We get our app!
app = Application.objects.get(name="myapp")
# We delete the old token
try:
old = AccessToken.objects.get(user=user, application=app)
except:
pass
else:
old.delete()
# We create a new one
my_token = generate_token()
# We create the access token
# (we could create a refresh token too the same way)
AccessToken.objects.create(user=user,
application=app,
expires=now() + timedelta(days=365),
token=my_token)
return "OK" # you can return your token as JSON here
else:
return "ERROR"
I'm just not sure about the way I generate the token, is this good practice? Well, in the mean time, it works!!

Maybe django-rest-framework-social-oauth2 is what you're looking for. This package depends on python-social-auth and django-oauth-toolkit, which you already use. I quickly scanned through the documentation, and it seems to implement just what you are trying to do.

I was doing React Native with expo and Django with Django REST framework. This blogpost ended being the way I solved registration (signup) with facebook https://medium.com/#gabriel_gamil/react-native-expo-django-facebook-authentication-sign-in-83625c49da7
tldr; use django-rest-auth https://django-rest-auth.readthedocs.io/en/latest/index.html
use Django-allauth https://django-allauth.readthedocs.io/en/latest/

Related

django_auth_adfs: get JWT token for the client on successful authentication

I have a Django application that doesn't have MVC pages and most of the data is served/posted via restful API powered by django-rest-framework. My userbase is in Azure single tenant AD, so I am trying to get the SSO going for them.
I am using django_auth_adfs to authenticate users against the Azure AD. Most of the stuff seems to work and the module takes care of the redirects and establishing the Django sessions for the client. Specifying the right permission_classes for the API ViewSets will make sure only authenticated users can access it it works fine via browser with proper django session cookie.
What I can't figure out is how to get the JWT token that I can give the UI client so that it could interact with the django-rest-framework API by supplying the JWT bearer and not relying on the session.
The documentation is not very specific on these details (besides the password grant that isn't quite relevant for my scenario).

How to integrate Facebook Login in django-graphql-jwt?

We have a django project that uses the Graphene-Django library to implement a GraphQL API in our project. This backend is accessed by our mobile apps. For the authentication of the apps, we use the django-graphql-jwt library, which is a JSON Web Token library in Django with GraphQL approach.
Now we want to implement the Facebook Login in our system and with it the authentication happens in Facebook. After authentication, what will be sent to our backend from the mobile app is only the email of the user. How can I register and authenticate the user in django-graphql-jwt without the password? Or is there a better workflow for this?
After authentication, what will be sent to our backend from the mobile app is only the email of the user.
Hey Al Ryan, this seems like a faulty implementation of OAuth, what you get back from facebook is a token you send that token to your server, and it will send it back to facebook to verify it's not faked, then only user can be logged in.
Otherwise anyone can call the server with a email and act as that user.
This is a library with social auth and JWT support, see if this helps.
I'm also sharing solution from my project
Create a facebookAuth named graphql mutation
Above mutation will take two params access_token and access_verifier
Send a GET request to this url f"https://graph.facebook.com/me?fields=name,email&access_token={access_token}"
If json response has a key errors, stop user from logging in.
Otherwise above response will contain email, use it to create/get a User object.
Now you simply need to return the JWT token from your mutate function.
To generate access and refresh tokens call this function jwt_encode, imported as from dj_rest_auth.utils import jwt_encode
above will return tuple access_token, refresh_token
Note I have used dj_rest_auth instead of django-graphql-jwt, but it's pretty equivalent you just need a function to sign the JWT, rest all is custom logic so better write yourself.
PS: OAuth is a sensitive entry-point for attackers so implement is securely, you can contact at atul7555[at]gmail.com for any assistance.

Secure authentication between ReactJS and Django

Been reading and watching quite a bit, and asking a lot of questions regarding ReactJS and Django.
This particularly helped me to understand the the flow of data from Django REST Framework to ReactJS and from ReactJS to Django REST Framework.
Django Forms and Authentication with Front-end Framework (AngularJS/ReactJS)
However, the one thing I am trying to understand is authentication to the Django REST Framework. I understand from the documentation that it has built in authentication. Since this is sensitive data, I would obviously want it protected people retrieving it just by going to http://www.my_site.com/info/api.
I would need to setup ReactJS to be the only thing that can request data from the API whether that is through a key or username/password credentials. I am just curious how this is handled? Obviously I don't want that hard coded in ReactJS because it will compile with the rest of ReactJS.
Here's how I'd approach it: I'd use a JSON Web Token (JWT) for authentication and authorization.
You'd use your back-end to protect ALL API requests from invalid JWT's except for routes where a user won't have a token (ie, registration/log-in pages).
Here's how the flow of the application will go:
A new user registers to your app with standard credentials such as email and password.
Your back-end will create a new user, sign a new JWT token (usually with the user's ID). You'll probably use a third-party library to sign/verify tokens (I don't have experience in the Django community but I am sure a quick Google search will give you answers). Your back-end will send back this token. This is the only time the back-end will receive email, passwords or any other sensitive information on registration.
From this point on React will only use this token for authorization. React will save this token somewhere (ie, localStorage) and send this token along with the other parts of a request to the API routes you created with your back-end. You'll send this token in the authorization headers in the request.
Your back-end will validate this token using a third-party library. If it's invalid the request stops and an unauthorized error is returned. If it's valid the request continues.
This achieves the following:
Your API routes are protected against unauthenticated users
Each request to your API is verified for authorized users which protects anyone from requesting any part of your API.
You can further solidify this by only allowing requests for users to modify their own data. For example, protect Suzy's profile from being modified by people other than herself by only allowing her token with her ID to modify her account/data.
Important Note- Your backend will never save these tokens in storage. It will verify the token on each request. Read more about JSON Web Tokens (JWT) and how it works.
Django Rest Framework has built-in token authentication and a third party package for JWT Token Auth.
If you the standard token auth would work for you, then it could be pretty simple with drf-redux-auth. If you need JWT for some reason, as suggested by Keith above, you could easily fork the above...

Generate an OAuth2 token in a view

Let's say I have an AngularJS application that consumes the REST API of a Django application.
The Django application has got a built-in OAuth2 provider that can be called to retrieve an access token and use the protected endpoints of the API. This provider is using django-oauth-toolkit.
Let's assume there is a registered client with "password" grant type, so that the end users only need to provide their credentials in the front-end in order to get an access token from the back-end.
At some point we want to add some support for social networks login and we decide to use python-social-auth (PSA) to that end. Here is the workflow I want to achieve:
The user logs in on Facebook from the front-end (via the Facebook SDK) and we get an access token back from the OAuth2 provider of Facebook.
We send the Facebook token to an endpoint of our REST API. This endpoint uses the Facebook token and django-social-auth to authenticate the user in our Django application (basically matching a Facebook account to a standard account within the app).
If the authentication succeeds, the API endpoint requests an access token from the OAuth2 provider for this newly authenticated user.
The Django access token is sent back to the front-end and can be used to access the REST API in exactly the same way that a regular user (i.e. logged in with his credentials) would do.
Now my problem is: how do I achieve step 3? I first thought I would register a separate OAuth2 client with Client Credentials Grant but then the generated token is not user-specific so it does not make sense. Another option is to use the TokenAuthentication from DRF but that would add too much complexity to my project. I already have an OAuth server and I don't want to set up a second token provider to circumvent my problem, unless this is the only solution.
I think my understanding of PSA and django-oauth-toolkit is not deep enough to find the best way of reaching my goal, but there must be a way. Help!
I managed to get something working using urllib2. I can't speak towards whether or not this is good practice, but I can successfully generate an OAuth2 token within a view.
Normally when I'd generate an access token with cURL, it'd look like this:
curl -X POST -d "grant_type=password&username=<user_name>&password=<password>" -u"<client_id>:<client_secret>" http://localhost:8000/o/token/
So we're tasked with making urllib2 accomplish this. After playing around for some bit, it is fairly straightforward.
import urllib, urlib2, base64, json
# Housekeeping
token_url = 'http://localhost:8000/auth/token/'
data = urllib.urlencode({'grant_type':'password', 'username':<username>, 'password':<password>})
authentication = base64.b64encode('%s:%s' % (<client_id>, <client_secret>))
# Down to Business
request = urllib2.Request(token_url, data)
request.add_header("Authorization", "Basic %s" % authentication)
access_credentials = urllib2.urlopen(request)
json_credentials = json.load(access_credentials)
I reiterate, I do not know if this is in bad practice and I have not looked into whether or not this causes any issues with Django. AFAIK this will do this trick (as it did for me).

Can I authenticate with OAuth in a Javascript app without saving a token on the client side with rauth?

I want to be able to authenticate users of an angular.js application using oauth, but I do not want to store any tokens on the frontend because I have seen that it can be fairly complicated to do so securely. Is there a way to pass some sort of credentials of a user to my django web application, where is can authenticate the user with some oauth provider and save that information in a session? To make it simple, here is the process I want
User is logged into some oauth provider, i.e. stackexchange
They click a "login with stackexchange" button on the front end angular app
Their login credentials are sent over to the django application through a restful api
The django app which receives these credentials attempts to get a token using rauth
If the server receives a token, the user is logged in and their information is saved in a session, otherwise they are given an error
Is this sort of process supported by OAuth2 providers?
Step 3 is incorrect: that authentication process is handled entirely off-site, on the OAuth provider's infrastructure, e.g. StackExchange.
This kind of flow is certainly possible. I would check out the Facebook example, which uses Flask, but provides a similar framework for how you might go about this in Django.