Anasayfa / Cyber Security / Secure Your Django App: A Step‑by‑Step Guide to Two‑Factor Authentication

Secure Your Django App: A Step‑by‑Step Guide to Two‑Factor Authentication

Django security

Two‑factor authentication (2FA) is the single most effective way to lock out attackers who have stolen a password. In Django, the ecosystem is mature and offers several battle‑tested packages that let you add 2FA with minimal code. This guide walks you through every step—from choosing a strategy to deploying the finished solution—so you can protect your users without sacrificing developer velocity.

What You’ll Need

  • A Django project (1.11+ is fine, but 3.x is recommended)
  • Python 3.8+ and pip
  • Access to your project’s settings and URL configuration
  • Optional: an SMS gateway (Twilio, Nexmo) or an email backend for backup methods
  • Optional: a QR code library (qrcode) for authenticator apps

Step 1: Pick a 2FA Strategy

First, decide which type of second factor you want to offer. The most common are:

  • Time‑based One‑Time Passwords (TOTP) via Google Authenticator or Authy
  • SMS OTP (less secure, but still better than password alone)
  • Email OTP (fallback for users without a phone)
  • Hardware tokens (YubiKey) or WebAuthn (FIDO2)

For this tutorial we’ll focus on TOTP using the django-two-factor-auth package, which also supports SMS and email out of the box. If you need hardware token support, you can later drop in django-otp-yubikey or django-webauthn.

Step 2: Install the Required Packages

Run the following pip command in your virtual environment:

pip install django-two-factor-auth django-otp django-phonenumber-field phonenumbers

Explanation of each:

  • django-two-factor-auth – The core 2FA framework.
  • django-otp – Handles OTP backends and storage.
  • django-phonenumber-field – Validates phone numbers for SMS.
  • phonenumbers – A dependency for parsing international numbers.

Step 3: Update INSTALLED_APPS and Middleware

Open settings.py and add the following:

INSTALLED_APPS += [
    "otp_token",
    "two_factor",
    "phonenumber_field",
]

Also add the middleware just before AuthenticationMiddleware:

MIDDLEWARE += [
    "otp.middleware.OTPMiddleware",
]

These additions enable OTP token models, the 2FA view mixins, and the middleware that intercepts login attempts.

Step 4: Configure URL Patterns

Create a two_factor_urls.py file in your project’s root (or inside an app) with:

from django.urls import path, include
from two_factor.urls import default as two_factor_urls

urlpatterns = [
    path("accounts/two_factor/", include(two_factor_urls)),
]

Then include this in your main urls.py:

urlpatterns += [
    path("", include("two_factor_urls")),
]

Now you’ll have a full set of 2FA URLs: /accounts/two_factor/setup/, /accounts/two_factor/mfa/, etc.

Step 5: Set Up the Login View to Trigger 2FA

The django-two-factor-auth package automatically wraps the Django login view when you add OTPMiddleware. But you can also use a custom view for more control. Here’s a minimal custom login that forces 2FA:

from django.contrib.auth import authenticate, login
from django.shortcuts import redirect, render
from two_factor.views import OTPLoginView

class CustomLoginView(OTPLoginView):
    template_name = "two_factor/login.html"

    def form_valid(self, form):
        user = authenticate(
            self.request,
            username=form.cleaned_data["username"],
            password=form.cleaned_data["password"],
        )
        if user is not None:
            login(self.request, user)
            return redirect("two_factor:setup")
        return self.form_invalid(form)

Hook this view into urls.py:

path("accounts/login/", CustomLoginView.as_view(), name="login"),

When a user logs in, they’ll be redirected to the 2FA setup page if they haven’t configured a second factor yet.

Step 6: Create Templates for 2FA Flow

Copy the default templates from django-two-factor-auth into your project’s templates/two_factor/ folder. The most important ones are:

  • setup.html – Shows the QR code and recovery codes.
  • login.html – Prompts for the OTP after password entry.
  • backup_codes.html – Lists backup codes.

To generate the QR code, install qrcode[pil]:

pip install qrcode[pil]

Ensure the template tags {% load static %} are present and that the QR image is rendered with:

Scan this QR code with your authenticator app

Step 7: Optional – Add SMS or Email OTP Backends

If you want to offer SMS as a fallback, configure Twilio in settings.py:

TWILIO_ACCOUNT_SID = "YOUR_SID"
TWILIO_AUTH_TOKEN = "YOUR_TOKEN"
TWILIO_PHONE_NUMBER = "+1234567890"

Then add the backend to OTP_BACKEND_CLASSES:

OTP_BACKEND_CLASSES = [
    "two_factor.backends.TOTPBackend",
    "two_factor.backends.SMSBackend",
]

For email OTP, set up Django’s email backend and add two_factor.backends.EmailBackend similarly.

Common Mistakes to Avoid

1. Skipping the OTPMiddleware – Without it, the login flow won’t trigger 2FA automatically.

2. Using the wrong URL namespacetwo_factor:setup must be referenced correctly; otherwise, redirects fail.

3. Not generating recovery codes – Users who lose their device need a backup; ensure recovery_codes.html is accessible.

4. Leaving default Django templates in place – They may not include QR code rendering; copy the package’s templates.

5. Using insecure OTP backends – SMS is vulnerable to SIM‑swap; consider adding email or hardware token options.

Tips and Tricks

  • Use django-otp-yubikey for YubiKey support. It requires a Yubikey API key and simple settings adjustments.
  • Leverage django-otp-webauthn for WebAuthn (FIDO2) support; it’s future‑proof and works in modern browsers.
  • Store recovery codes in a separate model and mark them as used after a single use to prevent reuse.
  • Implement rate‑limiting on OTP submission using Django’s django-ratelimit package to mitigate brute‑force attacks.
  • Use the django-two-factor-auth admin integration to see which users have 2FA enabled.

Frequently Asked Questions

Can I use 2FA without changing my existing login view?

Yes. By adding OTPMiddleware and including the two_factor_urls in your URLconf, the middleware intercepts login attempts and redirects users to the OTP prompt automatically.

Is SMS OTP secure enough for my application?

SMS is convenient but can be compromised via SIM‑swap or interception. It’s fine as a secondary method, but for high‑risk apps, prefer TOTP or WebAuthn. Consider offering both.

How do I test the 2FA flow locally?

Run Django’s development server, navigate to /accounts/login/, log in, and you’ll be prompted for a TOTP. Use the Google Authenticator app to scan the QR code. For SMS, you can use Twilio’s sandbox or the twilio-cli to capture messages.

Conclusion

Implementing two‑factor authentication in Django is a straightforward process thanks to the mature ecosystem of OTP packages. By following these steps, you’ll add a robust security layer that protects your users and satisfies compliance requirements. Remember to keep your dependencies up to date, monitor login attempts, and provide multiple recovery options to keep the user experience smooth. Happy coding, and stay secure!

Photo by Faisal on Unsplash

Etiketlendi: