When I started building web applications, user registration felt easy, just a form, some validations, and done. But as security concerns grew and user experience expectations changed, I realized adding an OTP (One-Time Password) verification step is essential. It not only enhances security but also improves trustworthiness, especially when dealing with sensitive data like emails or phone numbers.
In this article, I’ll walk you through how to implement user registration with OTP verification in Django, based on my years of experience. I’ll provide clear, practical examples tailored for developers in the USA, so you can build secure and reliable Django apps quickly.
Let’s get in!
Method for OTP Verification in User Registration
You might wonder, why to add OTP verification? In the US market, where data privacy laws and cybersecurity awareness are high, verifying users’ contact info is crucial. OTP helps:
- Confirm that the user owns the email or phone number they provide.
- Reduce fake or spam registrations.
- Add an extra layer of security during sign-up.
Implementing this in Django is easier than you might think, and I’ll show you two methods: using email-based OTP and SMS-based OTP, so you can pick what fits your project.
Read Check if Python Dictionary is Empty
Method 1: Email-Based OTP Verification in Django
Email OTP is the most common approach. Here’s how I usually set it up.
Step 1: Set Up Your Django Project
First, create a new Django project and app:
django-admin startproject otp_project
cd otp_project
python manage.py startapp accountsAdd accounts to your INSTALLED_APPS in settings.py.
Step 2: Configure Email Backend
For development, I use the console email backend to see OTPs in the terminal:
# settings.py
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'For production, configure SMTP settings with services like SendGrid or Amazon SES.
Step 3: Create User Registration Model and Form
We’ll use Django’s built-in User model, but create a profile to store OTP and verification status.
# accounts/models.py
from django.contrib.auth.models import User
from django.db import models
import random
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
otp = models.CharField(max_length=6, blank=True, null=True)
is_verified = models.BooleanField(default=False)
def generate_otp(self):
self.otp = f"{random.randint(100000, 999999)}"
self.save()Create signals to auto-create UserProfile when a User is created:
# accounts/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from .models import UserProfile
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)Register signals in apps.py:
# accounts/apps.py
from django.apps import AppConfig
class AccountsConfig(AppConfig):
name = 'accounts'
def ready(self):
import accounts.signalsCreate a registration form:
# accounts/forms.py
from django import forms
from django.contrib.auth.models import User
class UserRegistrationForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
confirm_password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = ['username', 'email', 'password']
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get("password")
confirm_password = cleaned_data.get("confirm_password")
if password != confirm_password:
raise forms.ValidationError("Passwords do not match.")Step 4: Create Views for Registration and OTP Verification
# accounts/views.py
from django.shortcuts import render, redirect
from django.contrib.auth.models import User
from django.core.mail import send_mail
from .forms import UserRegistrationForm
from .models import UserProfile
def register(request):
if request.method == 'POST':
form = UserRegistrationForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.set_password(form.cleaned_data['password'])
user.is_active = False # Deactivate account until verified
user.save()
profile = user.userprofile
profile.generate_otp()
send_mail(
'Your OTP Code',
f'Your OTP is {profile.otp}',
'no-reply@yourdomain.com',
[user.email],
fail_silently=False,
)
request.session['user_id'] = user.id
return redirect('verify_otp')
else:
form = UserRegistrationForm()
return render(request, 'accounts/register.html', {'form': form})
def verify_otp(request):
user_id = request.session.get('user_id')
if not user_id:
return redirect('register')
user = User.objects.get(id=user_id)
profile = user.userprofile
if request.method == 'POST':
input_otp = request.POST.get('otp')
if input_otp == profile.otp:
user.is_active = True
user.save()
profile.is_verified = True
profile.otp = ''
profile.save()
del request.session['user_id']
return redirect('login')
else:
error = "Invalid OTP. Please try again."
return render(request, 'accounts/verify_otp.html', {'error': error})
return render(request, 'accounts/verify_otp.html')Step 5: Add URLs and Templates
# accounts/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('register/', views.register, name='register'),
path('verify-otp/', views.verify_otp, name='verify_otp'),
]Include these URLs in your project’s urls.py.
Create simple HTML templates for registration and OTP verification with forms.
You can see the output in the screenshot below.


Check out Python Filter Not in Django
Method 2: SMS-Based OTP Verification Using Twilio
For US-based users, SMS OTP is highly effective. Here’s a quick overview of how I integrated Twilio for SMS OTP verification.
Step 1: Install Twilio
pip install twilioStep 2: Update the UserProfile Model to Store Phone Number
Add a phone number field and OTP logic similar to the email method.
Step 3: Configure Twilio Client
# accounts/utils.py
from twilio.rest import Client
def send_otp_via_sms(phone_number, otp):
account_sid = 'your_account_sid'
auth_token = 'your_auth_token'
client = Client(account_sid, auth_token)
message = client.messages.create(
body=f"Your OTP code is {otp}",
from_='+1234567890', # Your Twilio number
to=phone_number
)
return message.sidStep 4: Modify Registration and OTP Verification Views
Send OTP via SMS using the send_otp_via_sms function instead of email.
Read Union Operation on Django Models
Tips from My Experience
- Always set
user.is_active = Falseuntil verification to prevent unauthorized access. - Store OTPs securely and clear them after verification.
- Use session or temporary storage to track users between registration and OTP verification.
- For production, use reliable email/SMS services with proper error handling.
- Consider adding OTP expiration for better security.
Implementing OTP verification in Django might seem complex at first, but breaking it down into manageable steps makes it straightforward. Whether you choose email or SMS OTP, the key is to ensure a smooth user experience while maintaining security.
If you want reusable code and a clean structure, consider creating Django custom user models and integrating third-party packages like django-otp or django-allauth for extended features.
I hope this guide makes your Django user registration more secure and trustworthy!
You may read:
- Create a model in Django
- ModuleNotFoundError: No module named Django
- How to View Uploaded Files in Django

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.