Django REST Framework Authentication by Example with JSON Web Tokens (JWT) -- Words (805)

Django REST Framework JWT Example

Introduction

Django Rest Framework provides multiple mechanisms for authenticating users, in case you are new to this concept then simply put: authentication is the process of identifying a logged in user, while authorization is identifying if the user has authorized access to some server resource.

In this tutorial, we are going to see what's the DRF available mechanisms to authenticate users, what is the difference between DRF built in token based auth system and JWT auth and then how to add JSON Web Tokens authentication to Django Rest Framework.

How to Authenticate a User?

The general process of authenticating a user is done by simply checking if any user information or credentials are attached to an incoming (from the client) request.

DRF has already three mechanisms to authenticate users, lets look at each one of them:

  • Basic Authentication: It's very easy to setup but only recommended for testing purposes not for production. It's implemented in rest_framework.authentication.BasicAuthentication class. And works by base64 encoding the user login information i.e the username and the password and attach them to an HTTP Authorization Header (can then be retrieved from request.META.HTTP _ AUTHORIZATION).

  • Session Based Authentication: The traditional authentication mechanism and default one used by Django, which depends on cookies on the client to store the user session infomation once the user is logged in on the server. Cookies are not available for mobile and desktop apps and they are not accepted by browsers in the case of cross domains apps. Session Auth is implemented in rest_framework.authentication.SessionAuthentication class.

  • Token Based Authentication A token, which is a hashed (base64) set of information, is generated and sent to the client when the user logs in. Each time the user sends a request it attaches the token as an Authorization header. DRF associates users and generated token with a database table so it needs to query the database for every request to determine the association between a token and a user. You can reduce database access by caching the token but this doesn't scale well when the application gets bigger. Token Auth is implemented in rest_framework.authentication.TokenAuthentication class.

So what is the difference between DRF built in token based auth and JSON Web Tokens or JWT auth?

DRF built in token based auth, uses a database table to make associations between users and random takens, there is no way you can determine a user from the token itself since it's purely random.

JWT is a JSON data encoded using a secret so the server doesn't need to query database it can retrieve the associated user from the token itself, it's more efficient and sacles better than DRF built in token system.

So now that we have seen the difference between JWT and DRF built in token based auth systems, how can we implent JWT authentication with Django Rest Framework?

Unfortunatley, DRF has no built in JWT auth system but we can very easily add it to our project using django-rest-framework-jwt package which can be installed from pip.

pip install djangorestframework-jwt

Next you need to add: restframeworkjwt.authentication.JSONWebTokenAuthentication to DEFAULTAUTHENTICATIONCLASSES in REST_FRAMEWORK configuration object in settings.py:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
         'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
}

You can also customize many settings for djangorestframework-jwt using JWT_AUTH object. For example:

JWT_AUTH = { 
    'JWT_AUTH_HEADER_PREFIX': 'JWT',
    'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300)
}

Next you need to add different URLs for working with JWT to urls.py:

from django.conf.urls import url
from rest_framework_jwt.views import obtain_jwt_token
from rest_framework_jwt.views import refresh_jwt_token
from rest_framework_jwt.views import verify_jwt_token

urlpatterns = [
    url(r'^auth-jwt/', obtain_jwt_token),
    url(r'^auth-jwt-refresh/', refresh_jwt_token),
    url(r'^auth-jwt-verify/', verify_jwt_token),
]

How to Obtain a Token?

You can obtain a token by sending a POST request with username and password to http://localhost:8000/jwt-auth/ endpoint. Lets see an example using cURL:

$ curl --request POST \
  --url http://localhost:8000/jwt-auth/ \
  --header 'content-type: application/json' \
  --data '{"username": "born2code", "password": "jb395566"}'

  {"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImJvcm4yY29kZSIsInVzZXJfaWQiOjEsImVtYWlsIjoiIiwiZXhwIjoxNTAxNDE4OTYzfQ.VOLCx7uVNa5CXFLos2grecJV2CzrK4FyVj4ontjKUrw"}

P.S: You need to provide the credentials for an existing registered user.

How to Make API Requests?

For every request to a protected API resource you need to attach the obtained token to an HTTP Authorization header in the form of: JWT TOKEN

Th prefix is JWT by default but if you have specified another on JWT_AUTH configuration object:

JWT_AUTH = { 
    'JWT_AUTH_HEADER_PREFIX': 'Bearer',
}

You need to specify that one instead:

Bearer <token>

Using cURL you can specify an Authorization header with:

$ curl -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImJvcm4yY29kZSIsInVzZXJfaWQiOjEsImVtYWlsIjoiIiwiZXhwIjoxNTAxNDE4OTYzfQ.VOLCx7uVNa5CXFLos2grecJV2CzrK4FyVj4ontjKUrw" -X GET  http://localhost:8000/api/products/

If you can get the protected information then congratulations you have successfully setup JWT with Django Rest Framework.


I'm a web developer and technical writer. Passionate about modern JavaScript technologies and currently trying to develop all kind of apps (Web, Mobile and Desktop) with JavaScript.