Implementing OTP Verification Form with AJAX and Django Templates

One-Time Password (OTP) verification adds an extra layer of security to your web applications. In this tutorial, we’ll guide you through the process of creating an OTP verification form using AJAX and Django templates. This will allow users to receive an OTP via email or SMS and verify their identity before accessing certain features or data within your application.

Prerequisites

Before getting started, make sure you have the following installed:

  1. Python and Django: Ensure you have Python and Django set up on your development,., environment.
  2. Django django-otp package: Install it using pip:
   pip install django-otp
  1. A working email setup in your Django project for sending OTPs.

Step 1: Create a Django App

Start by creating a Django app within your project:

python manage.py startapp otp_verification

Step 2: Model for Storing OTPs

In your app’s models.py, define a model to store OTPs:

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

class OTP(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    otp = models.CharField(max_length=6)
    is_verified = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.otp

After defining the model, run migrations to create the database table:

python manage.py makemigrations
python manage.py migrate

Step 3: Create Django Views

In your app’s views.py, create views for sending and verifying OTPs:

from django.shortcuts import render, redirect
from django.http import JsonResponse
from django.contrib.auth.decorators import login_required
from django.core.mail import send_mail
from django.contrib.auth.models import User
import random
from .models import OTP

@login_required
def home(request):
    return render(request, 'home.html')

@login_required
def send_otp(request):
    if request.method == 'POST':
        user = request.user
        otp_code = ''.join(random.choice('0123456789') for _ in range(6))
        OTP.objects.create(user=user, otp=otp_code)

        # Send OTP via email
        subject = 'Your OTP for Verification'
        message = f'Your OTP is: {otp_code}'
        from_email = '[email protected]'  # Replace with your email
        recipient_list = [user.email]

        send_mail(subject, message, from_email, recipient_list)

        return JsonResponse({'message': 'OTP sent successfully'})
    else:
        return JsonResponse({'error': 'Invalid request method'})

@login_required
def verify_otp(request):
    if request.method == 'POST':
        user = request.user
        otp_code = request.POST.get('otp')

        otp_obj = OTP.objects.filter(user=user, otp=otp_code, is_verified=False).first()

        if otp_obj:
            otp_obj.is_verified = True
            otp_obj.save()
            return JsonResponse({'message': 'OTP verified successfully'})
        else:
            return JsonResponse({'error': 'Invalid OTP or OTP already verified'})
    else:
        return JsonResponse({'error': 'Invalid request method'})

Step 4: Create Template

Create templates for sending and verifying OTPs in your app’s templates folder:

home.html

{% extends "base.html" %}

{% block content %}
  <h2>OTP Verification</h2>
  <form id="send-otp-form">
    {% csrf_token %}
    <button type="submit">Send OTP</button>
  </form>
  <div id="otp-response"></div>

  <form id="verify-otp-form">
    {% csrf_token %}
    <label for="otp">Enter OTP:</label>
    <input type="text" id="otp" name="otp" required>
    <button type="submit">Verify OTP</button>
  </form>
  <div id="otp-verify-response"></div>
{% endblock %}

{% block scripts %}
<script>
  // JavaScript to handle sending OTP via AJAX
  document.getElementById('send-otp-form').addEventListener('submit', function (e) {
    e.preventDefault();

    fetch('{% url "send_otp" %}', {
      method: 'POST',
      headers: {
        'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
      },
    })
      .then(response => response.json())
      .then(data => {
        document.getElementById('otp-response').innerText = data.message;
      })
      .catch(error => {
        console.error('Error:', error);
        alert('An error occurred.');
      });
  });

  // JavaScript to handle verifying OTP via AJAX
  document.getElementById('verify-otp-form').addEventListener('submit', function (e) {
    e.preventDefault();

    const otp = document.getElementById('otp').value;

    fetch('{% url "verify_otp" %}', {
      method: 'POST',
      headers: {
        'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: `otp=${otp}`,
    })
      .then(response => response.json())
      .then(data => {
        document.getElementById('otp-verify-response').innerText = data.message;
      })
      .catch(error => {
        console.error('Error:', error);
        alert('An error occurred.');
      });
  });
</script>
{% endblock %}

base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}Your Website Title{% endblock %}</title>
    <!-- Add your CSS and JavaScript links here -->
</head>
<body>
    <header>
        <nav>
            <ul>
                <li><a href="/">Home</a></li>
                <!-- Add more navigation links here -->
            </ul>
        </nav>
        {% if user.is_authenticated %}
            <p>Welcome, {{ user.username }}! <a href="{% url 'logout' %}">Logout</a></p>
        {% else %}
            <p><a href="{% url 'login' %}">Login</a> | <a href="{% url 'signup' %}">Sign up</a></p>
        {% endif %}
    </header>

    <main>
        {% block content %}
        {% endblock %}
    </main>

    <footer>
        <!-- Add your footer content here -->
    </footer>
    
    <!-- Add any scripts here if needed -->
</body>
</html>

Step 5: Update URLs

In your app’s urls.py, define the URL s for the send and verify OTP views:

from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='home'),
    path('send-otp/', views.send_otp, name='send_otp'),
    path('verify-otp/', views.verify_otp, name='verify_otp'),
    # Add other URLs as needed
]

Step 6: Update Base Template

In your project’s base template (base.html), you can add a link to the “Send OTP” page for authenticated users:

{% if user.is_authenticated %}
  <p><a href="{% url 'send_otp' %}">Send OTP</a></p>
{% endif %}

Step 7: Test the Application

Start your Django development server:

python manage.py runserver

Now, when authenticated users visit the “Send OTP” page, they can send an OTP via email. After receiving the OTP, they can visit the “Verify OTP” page to enter the code for verification. If successful, they will see a confirmation message.

You can customize the templates and styles to match your project’s design, and you may expand this functionality to include phone-based OTPs or integrate third-party OTP providers for additional security options..

Blogs You Might Like to Read!