Christian BouvierNov 11, 2016

JSON Web Token (JWT) Authentication in a Django/AngularJS web app

No matter if you are an experienced developer or if you are starting your first app, there is a task that we all face someday in our life as developers: user’s authentication.

Nowadays, there are several kinds of authentication techniques available, and many of them could fit your needs. Nevermind, this post is not about authentication mechanisms, it is about how to implement JSON Web Token Authentication in an application with a Django-based backend, using a REST API to offer resources for an AngularJS frontend app (which fits very well in the Octobot’s technologies stack, and maybe in yours)

First of all, why JWT? Well, because it is a compact and self-contained way for securely transmitting information between parties as a JSON object. Compact is good (we all know that), but self-contained? The JWT payload contains all the required information about the user, avoiding the need to query the database more than once. This makes JWT lightweight, scalable and easy to use.

Once a user was successfully logged in to your application using a username and password, he/she obtains a JWT which should be sent in every further request to the backend as an Authorization Header, and this token will tell the backend who the user making the request is.

The following image (extracted from here) illustrates this process.

JWT Diagram

So, let me show you how simple it is to integrate this mechanism, starting from the Django backend:

BACKEND

  • Install django-rest-framework and django-rest-framework-jwt

    pip install djangorestframework djangorestframework-jwt
  • Add them to your Django’s installed apps (in settings.py)

    INSTALLED_APPS = ( ... 'Rest_framework', 'rest_framework_jwt', )
  • Configure django-rest-framework permissions to accept JSON Web tokens by default. (You could avoid this step and configure each endpoint independently).

    REST_FRAMEWORK = {
      'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
      ),
    }
  • Configure JWT additional settings. There are several parameters available. (SECRET_KEY Is the key used to sign the JWT. Make sure this is safe and not shared or public.)

    JWT_AUTH = {
    'JWT_SECRET_KEY': SECRET_KEY,
    'JWT_AUTH_HEADER_PREFIX': 'Bearer',
    }
  • URLs (urls.py). Let’s suppose that we have a set of public data which doesn’t requires authentication and will be under the /public-data endpoint, and another set that requires authentication under the /protected-data. Last, an authentication endpoint to get the JWT from a username and password (api-token-auth)

from django.conf.urls import url
from YOUR_APP.views import PublicDataViewSet, ProtectedDataViewSet

urlpatterns = [
    url(r'public-data', PublicDataViewSet),
    url(r'protected-data', ProtectedDataViewSet),
    url(r'^api-token-auth/', 'rest_framework_jwt.views.obtain_jwt_token'),
]
  • Finally, let’s create and setup the Viewsets mentioned (PublicDataViewSet and ProtectedDataViewSet). Note that the ProtectedDataViewSet has a permission class (IsAuthenticated, ) so only authenticated users could reach this data.
from django.contrib.auth.models import User
from YOUR_APP.serializers import YOUR_MODEL_Serializer
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated

class PublicDataViewSet(viewsets.ModelViewSet):
    queryset = YOUR_MODEL.objects.all()
    serializer_class = YOUR_MODEL_Serializer    

class ProtectedDataViewSet(viewsets.ModelViewSet):
    permission_classes = (IsAuthenticated, )
    queryset = YOUR_MODEL.objects.all()
    serializer_class = YOUR_MODEL_Serializer

And that’s it for the backend side. The magic is inside django-rest-framework.

FRONTEND

Now, we’ll setup the AngularJS frontend application.

  1. Install angular-storage and angular-jwt

    bower install a0-angular-storage angular-jwt
  2. Inject the dependencies in the angular app module.

    angular.module('frontendApp', [
    ...
    'angular-storage',
    'angular-jwt'
    ])
  3. Add an interceptor to include the JWT in every http request.

    .config(function($stateProvider, $urlRouterProvider, $httpProvider, jwtInterceptorProvider) {
    jwtInterceptorProvider.tokenGetter = function(store) {
      return store.get('token');
    };
    // Add a simple interceptor that will fetch all requests and add the jwt token to its authorization header.
    $httpProvider.interceptors.push('jwtInterceptor');
    });
  4. Finally, in your login method take care of save the token in the browser local storage. This way, the interceptor will be able to access it before each request, even after refresh the page or close the browser.

    $scope.login = function(){
      $http.post(
        "backend/api-token-auth/",
        {'username': $scope.username, 'password': $scope.password})
          .success(function(response) {
              $scope.logged = true;
              $scope.jwt = response.token;
              store.set('token', response.token);
          }).error(function(response, status) {
              $scope.logged = false;
          });
    }

That’s it!

We hope this information is useful to you!