OTP Verification in Django REST Framework using JWT and Cryptography
Last Updated :
08 Aug, 2024
In this article, we'll explore how to implement OTP verification in Django REST Framework using JWT and cryptography. We will guide you through the process of setting up your Django application to support OTP-based authentication, demonstrate how to integrate JWT for secure token management and show how cryptographic techniques can enhance the overall security of your authentication.
You can check this article to learn how to create a basic API using DRF.
Setting up the Project
Create a virtual environment, and Install Required Packages
pip install django djangorestframework cryptography django-environ pyjwt
App structure
Setting.py
Now, there's some additional setup required. We need to configure Rest Framework, JWT authentication, and Email settings. The code is used to configure JWT settings and also the configuration of email functionality. Paste the above code and don't forget to put your own email address and password. A good practice will be to put them in the .env file and load them using the Django-environ package.
Python
# settings.py file
from datetime import timedelta
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(days=60),
'REFRESH_TOKEN_LIFETIME': timedelta(days=50),
}
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
)
}
# for email functionality
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = <Your Email Address >
EMAIL_HOST_PASSWORD = <Your Password >
EMAIL_USE_TLS = True
serializers.py
Now, in the views.py file, we will create a view to handle the logic of the forgot password. In this view, we will take user's email address and send an OTP in user's mail. So let's create a serializer to take email of the user.
Python
# serializers.py
from rest_framework import serializers
class ForgotPasswordSerializer(serializers.Serializer):
email = serializers.CharField(max_length=100)
views.py
In the views.py, add the logic to process the user's email, create a token and send token on the mail.
Here, we will first get the email entered by the user and check in the User model if our user already exists. If not, then it will throw an exception. If yes, then we will create a random number using the random module. Then we will create a payload which will be a dictionary and we will pass it to the create token function. The payload contains the user id, user email, otp itself and the expiry. After successfully getting the token, we will send the otp to the user’s email and then send a json response containing the token to the frontend again.
We have used a create_token() function but we have not implemented it yet. We will add the encryption logic of user's detail in this function. The function will take the payload, encrypt the data and returns a token.
Python
# views.py
import random
import datetime
from django.shortcuts import get_object_or_404
from django.core.mail import send_mail
from django.conf import settings
from django.contrib.auth.models import User
from rest_framework import status
from rest_framework.generics import GenericAPIView
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework.response import Response
from .security import create_token, decrypt_token
from .serializers import ForgotPasswordSerializer, CheckOTPSerializer
class ForgetPasswordView(GenericAPIView):
serializer_class = ForgotPasswordSerializer
def post(self, request, *args, **kwargs):
serilaizer = self.serializer_class(data=request.data)
serilaizer.is_valid(raise_exception=True)
email = serilaizer.validated_data['email']
user = get_object_or_404(User, email=email)
otp = str(random.randint(100000, 999999))
print(otp)
payload = {
'user_id': user.id,
'email': user.email,
'otp': otp,
'exp': datetime.datetime.now() + datetime.timedelta(minutes=5)
}
token = create_token(payload)
send_mail(
'OTP for Forget Password',
f'Your Otp is {otp}',
settings.EMAIL_HOST_USER,
[user.email],
)
return Response({
'token': token
}, status=200)
urls.py
Add an url endpoint for the view:
Python
# urls.py
from django.urls import path
from .views import ForgetPasswordView
urlpatterns = [
path('forgot_password', ForgetPasswordView.as_view(), name='forgot-password'),
]
security.py
Let's now add the logic of create_token and decrypt_token methods. Create a file called security.py file and add the given code:
In the create_token() method, we have used the cryptogrophy module as well as the pyjwt module for creating an enhanced and secured feature for otp verification.
Now lets understand the functionality.
- Load the .env file using django-environ. The .env file must contain the SECRET key present in the settings.
- Generate a key using Fernet class. This key will change every time you save and reload the server. To avoid it you can keep in the .env file.
- cipher_suite=Fernet(key) will create a cipher suit object which has the encrypt and decrypt functions.
- The create_token function accepts the payload generated earlier and first creates a JWT token out of it using the SECRET_KEY of the django project.
- This token is then encrypted using the cipher_suite.encrypt() method and now, the encrypted jwt token and thus now we return encrypted_token .
- This adds a two level security to our data. The JWT token which has the actual data and the encryption which creates a cipher text of the JWT token itself. Hence even after sending to the frontend no one can view or decode the token.
Python
from cryptography.fernet import Fernet
import jwt
import os
import environ
from django.conf import settings
environ.Env.read_env(os.path.join(settings.BASE_DIR, '.env'))
env = environ.Env()
key = Fernet.generate_key()
cipher_suite = Fernet(key)
JWT_SECRET = env("SECRET_KEY")
def create_token(payload):
token = jwt.encode(payload, JWT_SECRET, algorithm='HS256')
encrypted_token = cipher_suite.encrypt(token.encode()).decode()
return encrypted_token
def decrypt_token(enc_token):
try:
dec_token = cipher_suite.decrypt(enc_token.encode()).decode()
payload = jwt.decode(dec_token, JWT_SECRET, algorithms=['HS256'])
return {'payload': payload, 'status': True}
except:
return {'status': False}
Some More Steps
To verify the otp, we will write a separate route named 'CheckOTPView'. In this view, we will take otp and token from the user. Let's create a serializer for the same in your serializers.py file.
Python
# ....
class CheckOTPSerializer(serializers.Serializer):
otp = serializers.CharField(max_length=6)
token = serializers.CharField()
Now, add the logic for otp verification in your views.py file.
Python
#...
class CheckOTPView(GenericAPIView):
serializer_class = CheckOTPSerializer
def post(self, request, *args, **kwargs):
serialzier = self.serializer_class(data=request.data)
serialzier.is_valid(raise_exception=True)
otp = serialzier.validated_data['otp']
enc_token = serialzier.validated_data['token']
data = decrypt_token(enc_token)
if data['status']:
otp_real = data['payload']['otp']
if otp == otp_real:
email = data['payload']['email']
user = User.objects.get(email=email)
access_token = str(RefreshToken.for_user(user).access_token)
return Response(
{
'access_token': access_token,
'status': True,
}, status=status.HTTP_200_OK)
else:
return Response({
'message': 'OTP didnt matched....'
}, status=status.HTTP_400_BAD_REQUEST)
else:
return Response({
'message': 'OTP expired...Try Again!!',
'status': False
}, status=status.HTTP_400_BAD_REQUEST)
Urls.py: Now, add the url route for this view.
Python
# urls.py
# ...
urlpatterns = [
#....
path('check_otp', CheckOTPView.as_view(), name='check-otp')
]
In the above code, we have taken the token and otp from the user. Upon successful verification, we are sending access token, else an error response.
The frontend application will save the encrypted token in the local_storage or in use state and whenever the user enters the otp it will send a HTTP POST request to the server and it will include the encrypted token and the otp entered by the user.
- On receiving the token it will send it to the decrypt_token function.
- The function will first decrypt the token and this decrypted token will be decoded using jtw.decode() method through which will get the original payload.
- If the user fails to enter the token within the given time frame of 5 minutes the jwt.decode() will through an exception using which we can identify in the Otp was entered before or after the time ends.
- If entered later then, we will send a response with status as 400.
- Else, the Otp entered by the user and present in the payload will be checked and if correct then we will find the user using the User model and generate a new access token using the RefreshToken class .
- Finally, we will send the access token to the frontend with status 200 OK.
Output:
Forget Password Endpoint
Check OTP Endpoint
Similar Reads
How to return custom JSON in Django REST Framework ?
In this article, we will create class-based views and combine this with the serializer class to return JSON representation for each HTTP request. For our class-based views, we will make use of a set of generic views, which help to achieve minimum lines code. Generic Classes and ViewsetsHTTP request
9 min read
Creating and Using Serializers - Django REST Framework
In Django REST Framework the very concept of Serializing is to convert DB data to a datatype that can be used by javascript. Serializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can then be easily rendered into JSON, XML or other conten
3 min read
Integrating Django with Reactjs using Django REST Framework
In this article, we will learn the process of communicating between the Django Backend and React js frontend using the Django REST Framework. For the sake of a better understanding of the concept, we will be building a Simple Task Manager and go through the primary concepts for this type of integrat
15+ min read
Adding Permission in API - Django REST Framework
There are many different scenarios to consider when it comes to access control. Allowing unauthorized access to risky operations or restricted areas results in a massive vulnerability. This highlights the importance of adding permissions in APIs. Â Django REST framework allows us to leverage permissi
7 min read
DictField in serializers - Django REST Framework
In Django REST Framework the very concept of Serializing is to convert DB data to a datatype that can be used by javascript. Every serializer comes with some fields (entries) which are going to be processed. For example if you have a class with name Employee and its fields as Employee_id, Employee_n
4 min read
Date and time fields in serializers - Django REST Framework
In Django REST Framework the very concept of Serializing is to convert DB data to a datatype that can be used by javascript. Every serializer comes with some fields (entries) which are going to be processed. For example if you have a class with name Employee and its fields as Employee_id, Employee_n
7 min read
JWT Authentication with Django REST Framework
JSON Web Token is an open standard for securely transferring data within parties using a JSON object. JWT is used for stateless authentication mechanisms for users and providers, this means maintaining session is on the client-side instead of storing sessions on the server. Here, we will implement t
2 min read
JSONField in serializers - Django REST Framework
In Django REST Framework the very concept of Serializing is to convert DB data to a datatype that can be used by javascript. Every serializer comes with some fields (entries) which are going to be processed. For example if you have a class with name Employee and its fields as Employee_id, Employee_n
4 min read
Pass Extra Arguments to Serializer Class in Django Rest Framework
Django Rest Framework (DRF) is a powerful toolkit for building web APIs. It provides serializers that translate complex data types such as Django queryset into native Python data types, which can then be rendered into JSON or other content types. However, there are cases when we need to pass extra a
4 min read
Implement Token Authentication using Django REST Framework
Token authentication refers to exchanging username and password for a token that will be used in all subsequent requests so to identify the user on the server side.This article revolves about implementing token authentication using Django REST Framework to make an API. The token authentication works
2 min read