Skip to main content

Command Palette

Search for a command to run...

Adding TOTP-Based 2FA to Django REST Framework with django-totp

Updated
3 min read
Adding TOTP-Based 2FA to Django REST Framework with django-totp
K
I am a backend developer working with Python and Django, focused on building reliable web applications while strengthening my foundations in data structures and system design.

Two-factor authentication (2FA) is becoming a standard requirement for modern applications, especially for APIs that use JWT authentication or separate frontend/backend architectures.

While working on Django REST Framework projects, I wanted a lightweight and API-focused way to add TOTP authentication without depending heavily on template-based flows or admin integrations.

So I built django-totp.

It is a reusable Django package that provides:

  • TOTP enrollment

  • QR generation

  • backup recovery codes

  • encrypted secret storage

  • DRF endpoints

  • helper utilities for multi-step authentication flows

PyPI: django-totp

Requirements

  • Python 3.12+

  • Django 5.0+

  • Django REST Framework 3.15+

Installation

Install the package from PyPI:

pip install django-totp

Add the apps to your Django settings:

INSTALLED_APPS = [
    # Django apps...
    "rest_framework",
    "django_totp",
]

Configure the Encryption Key

TOTP secrets and backup codes are stored using Fernet encryption.

Generate a key once

python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

Add it to your environment

TOTP_ENCRYPTION_KEY=your-generated-key

Load it in Django settings

import os

TOTP_ENCRYPTION_KEY = os.environ["TOTP_ENCRYPTION_KEY"]

Include the URLs

from django.urls import include, path

urlpatterns = [
    path("api/", include("django_totp.urls")),
]

Run migrations:

python manage.py migrate

Available Endpoints

The package provides endpoints for the full enrollment lifecycle.

Create Enrollment

POST /api/totp/create/

Creates a TOTP secret and returns an SVG QR code.

Example response:

{
    "svg": "<svg ...>...</svg>"
}

Confirm Enrollment

POST /api/totp/confirm/

Request:

{
    "input_code": "123456"
}

Successful confirmation returns backup recovery codes.

Disable TOTP

POST /api/totp/disable/

Disables TOTP and removes backup codes.

Rotate Backup Codes

POST /api/totp/rotate_backup_codes/

Generates a new backup code set.

Example Login Flow

A common authentication flow looks like this:

1. Validate username/password
2. Check whether the user has TOTP enabled
3. Issue a temporary challenge token
4. Ask for TOTP or backup code
5. Verify the code
6. Issue final JWT/session

The package includes helper utilities for this flow.

Example:

from django_totp.auth import (
    generate_challenge_token,
    is_totp_enabled,
)

from django_totp.totp import verify_totp_code

Other Utilities

Useful helpers you can import directly:

  • django_totp.auth

    • is_totp_enabled(user)

    • generate_challenge_token(user)

    • verify_challenge_token(token)

    • get_user_from_challenge_token(token)

  • django_totp.totp

    • generate_totp_secret()

    • verify_totp_code(user, input_code)

    • create_totp_setup(user)

    • confirm_totp_setup(user, input_code)

    • disable_totp(user)

  • django_totp.backup_code_utils

    • store_backup_codes(user, codes)

    • verify_backup_code(user, input_code)

    • rotate_backup_codes(user)

  • django_totp.encryption

    • generate_fernet_key()

    • resolve_fernet_key(default=None)

    • encrypt(value)

    • decrypt(value)

Features

The package currently includes:

  • Encrypted TOTP secret storage

  • QR generation for authenticator apps

  • Backup code generation and rotation

  • One-time-use backup code validation

  • DRF integration

  • Configurable issuer name

  • Endpoint throttling support

  • Signed temporary challenge tokens

Why I Built It

Many existing Django 2FA solutions are designed primarily for server-rendered applications.

I wanted something focused more on:

  • DRF APIs

  • JWT authentication flows

  • SPA/mobile frontends

  • reusable API endpoints

The goal was to keep the package relatively lightweight while still covering common 2FA requirements.

Feedback, issues, and contributions are welcome.