Implementing Email and Mobile OTP Verification in Django: A Comprehensive Guide
Published: 2024-07-03 13:13:53

 
In today's digital landscape, ensuring the authenticity of user accounts is paramount for web applications. One effective method to achieve this is through email and mobile number verification using One-Time Passwords (OTPs). This article will guide you through the process of implementing OTP verification in a Django project, explaining its importance and providing actionable steps for implementation.

## Table of Contents

1. [Introduction](#introduction)
2. [Why OTP Verification Matters](#why-otp-verification-matters)
3. [Prerequisites](#prerequisites)
4. [Step-by-Step Implementation](#step-by-step-implementation)
5. [Best Practices and Considerations](#best-practices-and-considerations)
6. [Conclusion](#conclusion)

## Introduction

One-Time Passwords (OTPs) are unique, temporary codes used to verify a user's identity. By implementing OTP verification for both email and mobile numbers, you add an extra layer of security to your application, ensuring that users have access to the contact methods they've provided.

## Why OTP Verification Matters

1. **Enhanced Security**: OTP verification significantly reduces the risk of unauthorized access and account takeovers.
2. **User Authentication**: It confirms that users have provided valid contact information, which is crucial for account recovery and communication.
3. **Fraud Prevention**: It helps prevent the creation of fake accounts, as users must prove ownership of their email and phone number.
4. **Compliance**: Many regulatory standards require multi-factor authentication, which OTP verification can help satisfy.
5. **User Trust**: Implementing strong security measures like OTP verification builds user confidence in your application.

## Prerequisites

Before we begin, make sure you have the following:

- Python 3.x installed
- Basic knowledge of Django
- A Django project set up (if not, we'll cover that in the steps)

## Step-by-Step Implementation

Let's dive into the process of implementing OTP verification in a Django project.

### Step 1: Set up the Django project and app

First, let's create a new Django project and app:

```bash
django-admin startproject otp_verification
cd otp_verification
python manage.py startapp user_auth
```

Add 'user_auth' to INSTALLED_APPS in your project's settings.py file:

```python
INSTALLED_APPS = [
    ...
    'user_auth',
]
```

### Step 2: Install required packages

We'll need a couple of additional packages:

```bash
pip install django-otp pyotp
```

### Step 3: Create the User model

In your `user_auth/models.py` file, create a custom user model:

```python
from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    email = models.EmailField(unique=True)
    mobile_number = models.CharField(max_length=15, unique=True)
    is_email_verified = models.BooleanField(default=False)
    is_mobile_verified = models.BooleanField(default=False)
    email_otp = models.CharField(max_length=6, null=True, blank=True)
    mobile_otp = models.CharField(max_length=6, null=True, blank=True)
```

### Step 4: Create OTP generation and verification functions

Create a new file `user_auth/utils.py`:

```python
import pyotp
from datetime import datetime, timedelta

def generate_otp():
    totp = pyotp.TOTP(pyotp.random_base32(), interval=300)  # 5 minutes validity
    return totp.now()

def verify_otp(otp, user_otp):
    return otp == user_otp
```

### Step 5: Create views for registration and OTP verification

In your `user_auth/views.py` file:

```python
from django.shortcuts import render, redirect
from django.contrib.auth import login
from .models import CustomUser
from .utils import generate_otp, verify_otp
from django.core.mail import send_mail
from django.conf import settings

def register(request):
    if request.method == 'POST':
        username = request.POST['username']
        email = request.POST['email']
        mobile_number = request.POST['mobile_number']
        password = request.POST['password']
        
        user = CustomUser.objects.create_user(username=username, email=email, 
                                              mobile_number=mobile_number, password=password)
        
        # Generate and save OTPs
        email_otp = generate_otp()
        mobile_otp = generate_otp()
        user.email_otp = email_otp
        user.mobile_otp = mobile_otp
        user.save()
        
        # Send email OTP
        send_mail(
            'Email Verification OTP',
            f'Your OTP for email verification is: {email_otp}',
            settings.EMAIL_HOST_USER,
            [email],
            fail_silently=False,
        )
        
        # Send mobile OTP (you'll need to integrate with an SMS service)
        # For this example, we'll just print it
        print(f"Mobile OTP: {mobile_otp}")
        
        return redirect('verify_otp', user_id=user.id)
    
    return render(request, 'register.html')

def verify_otp(request, user_id):
    user = CustomUser.objects.get(id=user_id)
    
    if request.method == 'POST':
        email_otp = request.POST['email_otp']
        mobile_otp = request.POST['mobile_otp']
        
        if verify_otp(email_otp, user.email_otp) and verify_otp(mobile_otp, user.mobile_otp):
            user.is_email_verified = True
            user.is_mobile_verified = True
            user.email_otp = None
            user.mobile_otp = None
            user.save()
            login(request, user)
            return redirect('home')
        else:
            return render(request, 'verify_otp.html', {'error': 'Invalid OTP'})
    
    return render(request, 'verify_otp.html')
```

### Step 6: Create URL patterns

In your `user_auth/urls.py` file:

```python
from django.urls import path
from . import views

urlpatterns = [
    path('register/', views.register, name='register'),
    path('verify_otp/<int:user_id>/', views.verify_otp, name='verify_otp'),
]
```

Don't forget to include these URLs in your project's main `urls.py` file.

### Step 7: Create templates

Create two HTML templates in your `templates` directory:

`register.html`:
```html
<form method="post">
    {% csrf_token %}
    <input type="text" name="username" placeholder="Username" required>
    <input type="email" name="email" placeholder="Email" required>
    <input type="text" name="mobile_number" placeholder="Mobile Number" required>
    <input type="password" name="password" placeholder="Password" required>
    <button type="submit">Register</button>
</form>
```

`verify_otp.html`:
```html
<form method="post">
    {% csrf_token %}
    <input type="text" name="email_otp" placeholder="Email OTP" required>
    <input type="text" name="mobile_otp" placeholder="Mobile OTP" required>
    <button type="submit">Verify OTP</button>
</form>
```

### Step 8: Configure email settings

In your project's `settings.py` file, add the following email configuration:

```python
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'  # Use your email provider's SMTP server
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'your_email@gmail.com'
EMAIL_HOST_PASSWORD = 'your_email_password'
```

Replace the placeholder values with your actual email credentials.

### Step 9: Run migrations

Apply the database migrations:

```bash
python manage.py makemigrations
python manage.py migrate
```

### Step 10: Run the server

Start the Django development server:

```bash
python manage.py runserver
```

## Best Practices and Considerations

1. **Security**: In a production environment, use more secure methods for storing OTPs, such as hashing them before saving to the database.
2. **SMS Integration**: Integrate with a reliable SMS service provider to send mobile OTPs.
3. **OTP Expiry**: Implement a mechanism to expire OTPs after a certain time period.
4. **Rate Limiting**: Implement rate limiting to prevent abuse of the OTP generation and verification endpoints.
5. **Error Handling**: Add robust error handling and user feedback for a smoother user experience.
6. **Resend OTP**: Provide an option for users to request a new OTP if they don't receive the first one.
7. **Progressive Enhancement**: Allow users to access limited functionality even if they haven't verified their contact methods, but require verification for sensitive actions.

## Conclusion

Implementing email and mobile OTP verification in your Django application significantly enhances security and user trust. By following this guide, you've learned how to set up a basic OTP verification system. Remember to adapt this implementation to your specific needs and always prioritize user data security.

As you continue to develop your application, consider integrating additional security measures and regularly updating your authentication methods to stay ahead of potential threats. With OTP verification in place, you're taking a crucial step towards building a more secure and trustworthy web application.

Connect with me on my social media platforms for more updates and insights:

- **Twitter**: [@rupeshmisra2002](https://twitter.com/rupeshmisra2002)
- **LinkedIn**: [Rupesh Mishra](https://www.linkedin.com/in/rupeshmishra2002)
- **GitHub**: [Rupesh Mishra](https://github.com/solvibrain)