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

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 verifying the identity of users, 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 available mechanisms, in DRF, to authenticate users? What is the difference between DRF built-in token-based authentication system and JWT authentication? And how to add JSON Web Tokens authentication to Django Rest Framework?

Different Ways to Authenticate Users?

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. Let's look at each one of them:

  • Basic authentication: It's very easy to setup but it's 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 user's name and the password then attach them to an HTTP Authorization Header (can then be retrieved from request.META.HTTP_ AUTHORIZATION).

  • Session based authentication: The traditional authentication mechanism and the default one used by Django, which depends on cookies on the client to store the user session information 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 authentication 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 tokens 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-base authentication is implemented in rest_framework.authentication.TokenAuthentication class.

What is JSON Web Token?

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely >transmitting information between parties as a JSON object. This information can be verified and trusted because it >is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair >using RSA.--https://jwt.io/introduction/

JWT Auth vs Token-based Auth

What is the difference between DRF built-in token-based authentication and JSON Web Tokens or JWT authentication?

Django Rest Framework built-in token-based authentication uses a database table to make associations between users and random tokens. There is no way you can determine a user from the token itself since it's purely random.

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

Adding JWT-based Authentication to Django Rest Framework

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

Unfortunately, DRF has no built in JWT authentication 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 the 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),
]

Obtaining a JWT Token

You can obtain a token by sending a POST request with the user's name and password to thz http://localhost:8000/jwt-auth/ endpoint. Let's see an example using cURL:


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

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

Please note that you need to provide the credentials for an existing registered user.

Making API Requests?

For every request to a protected API resource you need to attach the obtained token to requests, with an HTTP Authorization header, in the form of: JWT <THE_TOKEN>

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


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

You need to specify that one instead: Bearer

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.