Stackademic

Stackademic is a learning hub for programmers, devs, coders, and engineers. Our goal is to…

Follow publication

Implementing Two-Factor Authentication in Django Rest API with OTP Verification

--

In today’s digital landscape, securing user accounts is of paramount importance. One effective way to enhance security is by implementing Two-Factor Authentication (2FA). This article will guide you through the process of integrating 2FA into a Django Rest API, focusing on the use of a 6-digit temporary code (OTP).

Two-Factor Authentication (2FA) is a security mechanism designed to add an extra layer of protection to user accounts and sensitive information by requiring two distinct forms of identification before granting access. Typically, these factors fall into three categories: something you know (such as a password), something you have (like a mobile device or a security token), and something you are (biometric data such as fingerprints or facial recognition). The fundamental principle behind 2FA is to enhance security by mitigating the risks associated with single-factor authentication, where only a password is used.

One-Time Password (OTP) is a security concept that involves generating a unique and temporary code valid for a single use or session. It serves as an additional layer of authentication, typically delivered through channels like text messages, mobile apps, or email. Unlike static passwords, an OTP changes with each use or over a set time period, providing a dynamic and time-sensitive aspect to the authentication process. The recipient of the OTP must enter this code within the specified timeframe to gain access, adding a dynamic element that significantly enhances security. OTPs are widely employed in Two-Factor Authentication (2FA) systems, acting as the second factor alongside traditional passwords. This dynamic and time-bound nature of OTPs makes them a powerful tool in thwarting unauthorized access, phishing attempts, and enhancing overall digital security.

By introducing an additional verification step, even if one factor is compromised, the second factor acts as a safeguard, significantly reducing the likelihood of unauthorized access. This method plays a crucial role in safeguarding personal and organizational data, offering an effective defense against password-related vulnerabilities, phishing attacks, and unauthorized account access. Overall, the implementation of Two-Factor Authentication represents a proactive measure in fortifying digital identities and maintaining the integrity of sensitive information in an increasingly interconnected and vulnerable digital landscape.

Prerequisites:

Before implementing 2FA, ensure you have the following set up:

  • Django: Make sure you have a Django project and a Django Rest Framework (DRF) app.
  • User Model: Set up a custom user model with a one-to-one relationship with the User model.
  • Email: Ensure your application is capable of sending emails. Django provides tools for this, such as the send_mail function.
  • Django Rest Framework: Install and configure DRF for your project.

Install Required Packages:

Make sure to install the necessary packages:

pip install djangorestframework_simplejwt django-crontab

Refer to my articles on JWT and django-crontab to set things up.

Account Model:

# models.py
from django.contrib.auth.models import User
from django.db import models

class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
otp = models.CharField(max_length=6, blank=True)
otp_expiry_time = models.DateTimeField(blank=True, null=True)
# Other fields as needed

Serializers:

# serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
from .models import UserProfile

class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ('otp', 'otp_expiry_time', 'other_fields_from_user_model')

# Add UserProfileSerializer to your UserSerializer

Views:

# views.py
from datetime import timedelta
from django.utils import timezone
from django.contrib.auth import authenticate, login as django_login
from django.core.mail import send_mail
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from .models import UserProfile
from .serializers import UserProfileSerializer


def generate_random_digits(n=6):
return "".join(map(str, random.sample(range(0, 10), n)))

@api_view(['POST'])
@permission_classes([AllowAny])
def login(request):
email = request.data.get('email')
password = request.data.get('password')

user = authenticate(request, email=email, password=password)

if user is not None:
# User credentials are valid, proceed with code generation and email sending
user_profile = UserProfile.objects.get(user=user)

# Generate a 6-digit code and set the expiry time to 1 hour from now
verification_code = generate_random_digits
user_profile.otp = verification_code
user_profile.otp_expiry_time = timezone.now() + timedelta(hours=1)
user_profile.save()

# Send the code via email (use Django's send_mail function)
send_mail(
'Verification Code',
f'Your verification code is: {otp}',
'from@example.com',
[email],
fail_silently=False,
)

return Response({'detail': 'Verification code sent successfully.'}, status=status.HTTP_200_OK)

return Response({'detail': 'Invalid credentials.'}, status=status.HTTP_401_UNAUTHORIZED)

@api_view(['POST'])
@permission_classes([AllowAny])
def verify(request):
email = request.data.get('email')
password = request.data.get('password')
otp = request.data.get('otp')

user = authenticate(request, email=email, password=password)

if user is not None:
user_profile = UserProfile.objects.get(user=user)

# Check if the verification code is valid and not expired
if (
user_profile.verification_code == otp and
user_profile.otp_expiry_time is not None and
user_profile.otp_expiry_time > timezone.now()
):
# Verification successful, generate access and refresh tokens
django_login(request, user)
# Implement your token generation logic here

# Use djangorestframework_simplejwt to generate tokens
refresh = RefreshToken.for_user(user)
access_token = str(refresh.access_token)

# Reset verification code and expiry time
user_profile.otp = ''
user_profile.otp_expiry_time = None
user_profile.save()

return Response({'access_token': access_token, 'refresh_token': str(refresh)}, status=status.HTTP_200_OK)

return Response({'detail': 'Invalid verification code or credentials.'}, status=status.HTTP_401_UNAUTHORIZED)

Periodic Task:

Configure a periodic task using django-crontab to reset expired codes:

# tasks.py
from django_cron import CronJobBase, Schedule
from django.utils import timezone
from .models import UserProfile

class ResetExpiredCodesJob(CronJobBase):
schedule = Schedule(run_every_mins=60) # Set the schedule interval as needed
code = 'your_app.reset_expired_codes_job' # Replace 'your_app' with your app name

def do(self):
UserProfile.objects.filter(
code_expiry_time__lt=timezone.now(),
otp__isnull=False
).update(otp='', otp_expiry_time=None)

Run Migrations:

Run migrations to create necessary database tables for django-crontab:

python manage.py migrate

Run django-crontab:

Finally, run the following command to start the scheduled tasks:

python manage.py crontab add

By following these steps, you can enhance the security of your Django Rest API by implementing Two-Factor Authentication with temporary code verification. This additional layer of security helps protect user accounts from unauthorized access.

Stackademic

Thank you for reading until the end. Before you go:

  • Please consider clapping and following the writer! 👏
  • Follow us on Twitter(X), LinkedIn, and YouTube.
  • Visit Stackademic.com to find out more about how we are democratizing free programming education around the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Published in Stackademic

Stackademic is a learning hub for programmers, devs, coders, and engineers. Our goal is to democratize free coding education for the world.

No responses yet

Write a response