UPDATED November 25, 2015
This post is part 2 of a series on using OAuth with Django REST Framework. Part 1 is the series overview and I share some of my thoughts on the process. Part 3 is about social auth (e.g., Facebook, Twitter authentication) using DRF. Part 4 offers a richer explanation of server vs. client oauth flows, and part 5 is about integrating parts 2 & 3.
Here at Yeti we build our APIs with Django REST Framework and use the OAuth2 scheme using Django OAuth Toolkit. I'm no OAuth expert and it took me awhile to figure it all out — in fact, this is the second version of this post! Hopefully this blog post will save others some trouble; it will show you everything you need to set up OAuth2 for your own application using DRF.
This post is NOT about setting up authentication with third-party services like Facebook (see Part 3 instead). It is also not about creating an authentication service for apps other than your own to use.
I'm assuming you already have a User model and have DRF setup.
Setting up OAuth for your own application can be confusing. For me, I was used to oauth flows with third-party services like Facebook and Twitter, but it took me a bit to wrap my head around how that would work for my app and our users. It doesn't help that oauth can be implemented in different ways (1.0 vs 2.0, different grant types, etc.).
Essentially, the way OAuth works, there needs to be an application that a user is authenticating with. But now our app is the service providing authentication, so what is the appilcation supposed to be? Nothing really. It's just a dummy application that we set up. You can think of it as representing your front-end application if that helps.
In your Django admin or shell, you can create a new oauth2_provider.models.Application. Set client type to "public" and grant type to "resource owner password based". The name can just be whatever makes sense to you (e.g., "iOS App", "JS Frontend"), and the application should be owned by your admin user.
Note: The Django OAuth Toolkit tutorial says to make the client type "confidential". You can if you want, but since this app is actually going to be living on a public, insecure client (browser, mobile device), it makes more sense for the client type to be "public".
You don't have to use this, you're welcome to just make a user in the admin, but here's a full example:
from rest_framework import serializers
class SignUpSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'password')
write_only_fields = ('password',)
from rest_framework import generics
from permissions import IsAuthenticatedOrCreate
class SignUp(generics.CreateAPIView):
queryset = User.objects.all()
serializer_class = SignUpSerializer
permission_classes = (IsAuthenticatedOrCreate,)
from rest_framework import permissions
class IsAuthenticatedOrCreate(permissions.IsAuthenticated):
def has_permission(self, request, view):
if request.method == 'POST':
return True
return super(IsAuthenticatedOrCreate, self).has_permission(request, view)
urlpatterns = patterns('',
url(r'^sign_up/$', views.SignUp.as_view(), name="sign_up"),
)
request:
{
"username": "django.pony",
"password": "djangsta"
}
response:
{
"username": "django.pony"
}
The DOT docs say that you can get your token with the following request:
curl -X POST -d "grant_type=password&username=<user_name>&password=<password>" http://<client_id>:<client_secret>@localhost:8000/o/token/
Let's translate that.
The mechanics of making this request with your Angular / iOS / whatever client are beyond the scope of this blog post, but I use a tool called Postman when playing around with APIs and the request looks like this:
You should get a response that looks like this:
{
"access_token": "THIS IS THE IMPORTANT PART",
"token_type": "Bearer",
"expires_in": 36000,
"refresh_token": "xyz456",
"scope": "read write groups"
}
From here out, using your token is straightforward. To make an authenticated request, just pass the Authorization header in your requests. It's value will be "Bearer YOUR_ACCESS_TOKEN".
The last missing piece of this is handling login. If your token expires or a user logs out of your application, you'll need to get a new token. Logging in works the exact same as the last request to /o/token/: you pass client id, grant type, username, and password, and you'll get a fresh access token back.
Remember, the only thing that lets the server know that your subsequent requests are authorized is that token you are passing along. To log out (or make an unauthorized request), just delete the access token on your front end.
These are the endpoints we just set up:
EndpointAuthRequestResponsesign_up/username, passwordusernameo/token/username, password, client_idtoken (and some other stuff)everything elseOAuth2 (formatted as: Bearer YOUR_TOKEN)
You should now be able to sign up, then hit the o/token/ endpoint, and then be on your merry way.