Django : How To Secure Your API Using JWT Authentication In Django And Django Rest Framework

Feb 11 2023 . 5 min read

JWT (Json Web Token) is one of the most popular token based authentication system. A Third party package for JWT authentication is djangorestframework-simplejwt which is used to implement jwt auth in django project.

Installation


First we need to install simplejwt package in our system by just hitting this command in terminal.

pip install djangorestframework-simplejwt

Setup simpleJWT package

To setup djangorestframework-simplejwt package follow these steps :


  1. Open settings.py file and add rest_framework_simplejwt under INSTALLED_APPS.
    settings.py
    1. INSTALLED_APPS = [
    2.    ...
    3.    'rest_framework_simplejwt'
    4.    ...
    5. ]

  2. Now your django project must be configured to use the library. In settings.py, add the authentication classes:
    settings.py
    1. REST_FRAMEWORK = [
    2.    ...
    3.    'DEFAULT_AUTHENTICATION_CLASSES': (
    4.      ...
    5.       'rest_framework_simplejwt.authentication.JWTAuthentication'
    6.      ...
    7.      )
    8.    ...
    9. ]

Agenda

We are going to create three API's here :


  1. Signup Api - That will create an user.

  2. Login Api - That will generate a access and refresh token for authorised user.

  3. Student Api - That will show the student data using the access token.

1. Signup API

To create a signup API, please follow these steps :


Create a serializers.py file within your app level. Let's create a ModelSerializer for Signup.

serializers.py
  1. from rest_framework import serializers
  2. from django.contrib.auth.models import User
  3. from django.contrib.auth.hashers import make_password
  4. class SignupSerializer(serializers.ModelSerializer):
  5.     """override create method to change the password into hash."""
  6.     def create(self, validated_data):
  7.             validated_data["password"] = make_password(validated_data.get("password"))
  8.             return super(SignupSerializer, self).create(validated_data)
  9.     class Meta:
  10.             model = User
  11.             fields = ['username','password']

Here in SignupSerializer class we have override the create method and convert our string password into hash using make_password() function.



Next open a views.py file, and Let's create a APIView class for Signup.

views.py
  1. from rest_framework.views import APIView
  2. from django.contrib.auth.models import User
  3. from rest_framework.response import Response
  4. from rest_framework import status
  5. from .serializers import SignupSerializer
  6. class SignupAPIView(APIView):
  7.     """This api will handle signup"""
  8.     def post(self,request):
  9.             serializer = SignupSerializer(data = request.data)
  10.             if serializer.is_valid():
  11.                     """If the validation success, it will created a new user."""
  12.                     serializer.save()
  13.                     res = { 'status' : status.HTTP_201_CREATED }
  14.                     return Response(res, status = status.HTTP_201_CREATED)
  15.            res = { 'status' : status.HTTP_400_BAD_REQUEST, 'data' : serializer.errors }
  16.            return Response(res, status = status.HTTP_400_BAD_REQUEST)


Next open an urls.py file and let's create an endpoint for signup api.

urls.py
  1. from django.urls import path
  2. from . import views
  3. urlspatterns = [
  4.     path("api/user/signup/", views.SignupAPIView.as_view(), name="user-signup"),
  5.   ]

2. Login API

Now we will going to create a Login Api that will basically generate an access token and refresh token, so that we can use access token to access the students data.


To create a login api, follow these steps :


Open a serializers.py file and let's create a ModelSerializer for Login.

serializers.py
  1. from rest_framework import serializers
  2. from django.contrib.auth.models import User
  3. from django.contrib.auth.hashers import make_password
  4. class LoginSerializer(serializers.ModelSerializer):
  5.     username = serializers.CharField()
  6.     class Meta:
  7.             model = User
  8.             fields = ['username','password']


Next, create a helpers.py file and let's create a get_tokens_for_user() function that will generate access token and refresh token for authenticate user.

helpers.py
  1. from rest_framework_simplejwt.tokens import RefreshToken
  2. from django.contrib.auth.models import User
  3. from django.contrib.auth.hashers import make_password
  4. def get_tokens_for_user(user):
  5.        refresh = RefreshToken.for_user(user)
  6.        return {
  7.               "username": user.username,
  8.               "refresh": str(refresh),
  9.               "access": str(refresh.access_token)
  10.               }


Next, open a views.py file, and Let's create a APIView class for Login.

views.py
  1. from rest_framework.views import APIView
  2. from django.contrib.auth.models import User
  3. from rest_framework.response import Response
  4. from rest_framework import status
  5. from .serializers import LoginSerializer
  6. from .helpers import get_tokens_for_user
  7. from django.contrib.auth import authenticate
  8. class LoginAPIView(APIView):
  9.     """This api will handle login and generate access and refresh token for authenticate user."""
  10.     def post(self,request):
  11.             serializer = LoginSerializer(data = request.data)
  12.             if serializer.is_valid():
  13.                     username = serializer.validated_data["username"]
  14.                     password = serializer.validated_data["password"]
  15.                     user = authenticate(request, username=username, password=password)
  16.                     if user is not None:
  17.                         res_data = get_tokens_for_user(User.objects.get(username=username))
  18.                         response = {
  19.                                "status": status.HTTP_200_OK,
  20.                                "message": "success",
  21.                                "data": res_data
  22.                                }
  23.                         return Response(response, status = status.HTTP_200_OK)
  24.                     else :
  25.                         response = {
  26.                                "status": status.HTTP_401_UNAUTHORIZED,
  27.                                "message": "Invalid Email or Password",
  28.                                }
  29.                         return Response(response, status = status.HTTP_401_UNAUTHORIZED)
  30.             response = {
  31.                  "status": status.HTTP_400_BAD_REQUEST,
  32.                  "message": "bad request",
  33.                  "data": serializer.errors
  34.                  }
  35.             return Response(response, status = status.HTTP_400_BAD_REQUEST)


Next, open an urls.py file and let's create an endpoint for login api.

urls.py
  1. from django.urls import path
  2. from . import views
  3. urlspatterns = [
  4.     path("api/user/login/", views.LoginAPIView.as_view(), name="user-login"),
  5.   ]


3. Students Api

Now we will going to create our Students Api that will basically return students data.


Open a models.py file and let's define student model.

models.py
  1. from django.db import models
  2. class Student(models.Model):
  3.        name = models.CharField(max_length = 100)
  4.        email = models.EmailField(max_length = 277)
  5.        created_at = models.DateTimeField(auto_now_add = True, null = True)
  6.        updated_at = models.DateTimeField(auto_now_add = True, null = True)
  7.        def __str__(self):
  8.               return str(self.name)


Hit these commands to create student table.

python manage.py makemigrations
python manage.py migrate


Next open a serializers.py file and let's create a ModelSerializer for Student.

serializers.py
  1. from rest_framework import serializers
  2. from .models import Student
  3. class StudentSerializer(serializers.ModelSerializer):
  4.     class Meta:
  5.             model = Student
  6.             fields = ['name','email','created_at','updated_at']


Next, open a views.py file, and Let's create a APIView class for Student.

views.py
  1. from rest_framework.views import APIView
  2. from rest_framework.response import Response
  3. from rest_framework import status
  4. from .serializers import StudentSerializer
  5. from .models import Student
  6. from rest_framework.permissions import IsAuthenticated
  7. class StudentAPIView(APIView):
  8.     """This api will handle student"""
  9.     permission_classes = [ IsAuthenticated ]
  10.     def get(self,request):
  11.             data = Student.objects.all()
  12.             serializer = StudentSerializer(data, many = True)
  13.             response = {
  14.                    "status": status.HTTP_200_OK,
  15.                    "message": "success",
  16.                    "data": serializer.data
  17.                    }
  18.             return Response(response, status = status.HTTP_200_OK)

We have to must add the IsAuthenticated permission to our student api. It means to get the students data, ideally you need to pass the access token, that you got at the time of login.



Next, open an urls.py file and let's create an endpoint for student api.

urls.py
  1. from django.urls import path
  2. from . import views
  3. urlspatterns = [
  4.     path("api/students/", views.StudentAPIView.as_view(), name="api-student"),
  5.   ]

Conclusion

This is how you can secure your all api's using jwt token, just you need to only add IsAuthenticated to permission_classes, and set the simplejwt to authenticate_classess in settings file.