Skip to content

Add MFA/2FA support via standalone sub-package#728

Merged
iMerica merged 25 commits intomasterfrom
feature/mfa
Mar 15, 2026
Merged

Add MFA/2FA support via standalone sub-package#728
iMerica merged 25 commits intomasterfrom
feature/mfa

Conversation

@iMerica
Copy link
Copy Markdown
Owner

@iMerica iMerica commented Feb 15, 2026

Summary

Adds opt-in TOTP-based MFA as a standalone sub-package (dj_rest_auth.mfa). If you don't enable it, nothing changes for you.

This is a big diff (~6k lines added), but the majority of that is the demo app overhaul (new Next.js SPA + Docker setup, replacing the old React app). The actual MFA code is roughly 1,700 lines including tests.

How it works

Users add dj_rest_auth.mfa to INSTALLED_APPS, install the with-mfa extra, and swap LoginView for MFALoginView in their URL config. MFA is per-user. Users who haven't activated TOTP go through the normal login flow.

For MFA-enabled users, login returns a short-lived signed ephemeral token instead of a real auth token. The client then sends that token plus a TOTP or recovery code to /mfa/verify/ to complete authentication.

Endpoints

  • POST /mfa/verify/ - complete MFA login with ephemeral token + code
  • GET /mfa/totp/activate/ - get setup payload (secret, totp_url, activation_token)
  • POST /mfa/totp/activate/ - confirm activation with activation_token + code
  • POST /mfa/totp/deactivate/ - disable TOTP (requires valid code)
  • GET /mfa/status/ - check whether MFA is enabled
  • GET /mfa/recovery-codes/ - view unused recovery codes
  • POST /mfa/recovery-codes/regenerate/ - regenerate codes

What changed since this PR was opened

  • Timing-safe comparisons (hmac.compare_digest) for TOTP and recovery code validation
  • TOCTOU race condition fix on recovery code consumption using select_for_update() with atomic transactions
  • Signed, user-bound activation tokens so the TOTP secret can't be swapped between init and confirm
  • Guard against silent TOTP replacement if MFA is already enabled
  • Audit logging for all sensitive MFA events (activation, deactivation, failed verifications, recovery code use)
  • last_used_at tracking on authenticator records
  • JWT support tested and working
  • Patches allauth for CVE-2026-27982
  • Demo app fully rebuilt (Next.js + Docker Compose, replaces old CRA app)
  • Test count up to 48+

Security notes

  • Ephemeral tokens are signed with Django's Signer, default 5 min TTL
  • TOTP secrets are signed before being stored in the database
  • Recovery codes use HMAC-SHA256 with a random seed, consumed atomically
  • TOTP replay protection via last-code tracking
  • All MFA events are audit-logged with user ID, IP, and user agent

Dependencies

  • pyotp>=2.9.0 via the with-mfa extra (not a core dependency)
  • qrcode is fully optional

Tests

48+ tests covering activation, deactivation, login flows (with and without JWT), expired/invalid tokens, replay attacks, race conditions, and audit logging. MFA tests use a separate URL config and @modify_settings so they don't touch the standard test suite.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds comprehensive TOTP-based Multi-Factor Authentication (MFA) support to dj-rest-auth as an optional sub-package (dj_rest_auth.mfa). The implementation is opt-in and backward compatible - existing code is unaffected except for new settings defaults in app_settings.py and optional extras in setup.py.

Changes:

  • Adds dj_rest_auth.mfa sub-package with models, views, serializers, and URLs for TOTP and recovery code authentication
  • Implements ephemeral token flow: users with MFA enabled receive a temporary signed token instead of immediate authentication, which must be exchanged with a valid TOTP/recovery code at /mfa/verify/
  • Includes comprehensive documentation in RST format and README with setup instructions, API endpoint descriptions, and usage examples

Reviewed changes

Copilot reviewed 18 out of 20 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
setup.py Adds with-mfa extras requiring pyotp>=2.9.0 and qrcode>=8.0
docs/mfa.rst Complete 304-line MFA documentation covering installation, endpoints, configuration, and examples
docs/index.rst Adds MFA documentation link to main index
docs/configuration.rst Documents all MFA-related configuration settings
dj_rest_auth/tests/test_mfa.py Comprehensive test suite with 28 tests including 19-step end-to-end flow
dj_rest_auth/tests/requirements.txt Adds pyotp test dependency
dj_rest_auth/tests/mfa_urls.py URL configuration for MFA tests
dj_rest_auth/mfa/views.py Implements MFALoginView, MFAVerifyView, TOTPActivateView, TOTPDeactivateView, MFAStatusView, and recovery code management views
dj_rest_auth/mfa/utils.py Ephemeral token creation/verification and MFA status checking utilities
dj_rest_auth/mfa/urls.py URL patterns for all MFA endpoints
dj_rest_auth/mfa/totp.py TOTP secret generation, validation, and management logic
dj_rest_auth/mfa/serializers.py Serializers for MFA verification, TOTP activation/deactivation, status, and recovery codes
dj_rest_auth/mfa/recovery_codes.py Recovery code generation, validation, and management with seed-based deterministic generation
dj_rest_auth/mfa/models.py Authenticator model storing TOTP secrets and recovery code data in JSONField
dj_rest_auth/mfa/migrations/0001_initial.py Initial migration creating Authenticator model with unique constraint on (user, type)
dj_rest_auth/mfa/apps.py AppConfig for the MFA sub-package
dj_rest_auth/mfa/init.py Empty init file for MFA package
dj_rest_auth/mfa/migrations/init.py Empty init file for migrations
dj_rest_auth/app_settings.py Adds MFA-related default settings and serializer import strings
README.md Adds Quick MFA Setup section with installation instructions and login flow diagram

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread dj_rest_auth/mfa/views.py
Comment thread docs/mfa.rst Outdated
Comment thread dj_rest_auth/mfa/recovery_codes.py Outdated
Comment thread dj_rest_auth/mfa/views.py
Comment thread dj_rest_auth/mfa/recovery_codes.py Outdated
Comment thread dj_rest_auth/mfa/serializers.py
Comment thread dj_rest_auth/mfa/recovery_codes.py Outdated
Comment thread dj_rest_auth/mfa/models.py
Comment thread dj_rest_auth/mfa/views.py
Comment thread setup.py Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 20 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread dj_rest_auth/mfa/serializers.py Outdated
Comment thread dj_rest_auth/mfa/views.py
Comment thread dj_rest_auth/mfa/views.py
Comment thread dj_rest_auth/mfa/views.py
Comment thread dj_rest_auth/mfa/serializers.py
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 20 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread dj_rest_auth/tests/test_mfa.py Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 21 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread dj_rest_auth/mfa/utils.py
Comment thread dj_rest_auth/mfa/serializers.py
Comment thread dj_rest_auth/mfa/views.py Outdated
Comment thread dj_rest_auth/mfa/views.py Outdated
Comment thread dj_rest_auth/mfa/views.py Outdated
Comment thread dj_rest_auth/mfa/views.py
Comment thread dj_rest_auth/mfa/audit.py Fixed
Comment thread dj_rest_auth/tests/test_mfa.py Fixed
Comment thread dj_rest_auth/tests/test_mfa.py Fixed
Comment thread dj_rest_auth/tests/test_mfa.py Fixed
Comment thread dj_rest_auth/tests/test_mfa.py Fixed
@iMerica iMerica merged commit d7304af into master Mar 15, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants