django-two-factor-auth

repository·master·Indexed 23 days ago

https://github.com/jazzband/django-two-factor-auth

A complete two-factor authentication (2FA) implementation for Django applications leveraging django-otp. It provides core views for the 2FA lifecycle, support for TOTP, SMS/Phone (via Twilio), Email, and WebAuthn security keys. Features include 'remember browser' functionality, OTP enforcement for the Django admin via AdminSiteOTPRequired, and tools to restrict view access to verified users.

Tokens
5K
Snippets
9
Records
30
Agent score
84%

What's inside django-two-factor-auth

  1. Overview of Django Two-Factor Authentication

    master

    Django Two-Factor Authentication provides a complete two-factor authentication (2FA) solution for Django projects. It is built on top of django-otp and integrates with django.contrib.auth to provide a user experience similar to Google's Two-Step Authentication.

    Supported authentication methods include:

    • Phone calls
    • Text messages (SMS)
    • Token generator apps (e.g., Google Authenticator)
    • Hardware tokens (e.g., YubiKey)

    Note: SMS integration requires Twilio.

  2. Customize the default 2FA device picker

    master

    By default, the application uses a built-in policy to choose a user's primary device. It looks for a device named "default", then the most-recently-used non-backup device, then the non-backup device with the lowest persistent_id. Backup devices (StaticDevice or devices named "backup") are excluded.

    You can override this by setting TWO_FACTOR_DEFAULT_DEVICE_PICKER to a dotted path of a callable. The callable receives the list of confirmed devices and must return one device or None. You can use two_factor.utils.primary_device_candidates to implement your own logic while maintaining the standard backup-exclusion behavior.

    # myapp/settings.py
    TWO_FACTOR_DEFAULT_DEVICE_PICKER = "myapp.utils.passkey_first_picker"
    
    # myapp/utils.py
    from two_factor.utils import primary_device_candidates
    
    def passkey_first_picker(devices):
        candidates = primary_device_candidates(devices)
        # Prefer WebAuthn over TOTP regardless of recency.
        webauthn = [d for d in candidates if d.__class__.__name__ == "WebauthnDevice"]
        return webauthn[0] if webauthn else (candidates[0] if candidates else None)
  3. Install django-two-factor-auth

    master

    Install the core package using pip:

    $ pip install django-two-factor-auth

    This project depends on django-phonenumber-field. You must either install phonenumbers or phonenumberslite manually, or install the package with the appropriate extras:

    $ pip install django-two-factor-auth[phonenumbers]
    # OR
    $ pip install django-two-factor-auth[phonenumberslite]
  4. Limit view access to two-factor-enabled users

    master

    You can restrict access to specific views so that only users who have successfully verified via two-factor authentication can access them. This can be achieved using a decorator for function-based views, a mixin for class-based views, or by manually checking the user's verification status in the request.

    from django_otp.decorators import otp_required
    
    @otp_required
    def my_view(request):
        pass
  5. Setup Yubikey support

    master

    To enable Yubikey support, follow these steps:

    1. Install the plugin

    $ pip install django-otp-yubikey

    2. Update INSTALLED_APPS

    Add the following to your settings:

    INSTALLED_APPS = [
        ...
        'otp_yubikey',
        'two_factor.plugins.yubikey',
    ]

    3. Configure Validation Service

    Yubikeys require a validation service (typically YubiCloud).

    • Via Django Admin: Navigate to YubiKey validation services and add an item with the name default.
    • Via Django Shell: You can create the default service using the following command:
    from otp_yubikey.models import ValidationService
    ValidationService.objects.create(name='default', use_ssl=True, param_sl='', param_timeout='')
  6. Enforce two-factor authentication in the Django Admin

    master

    By default, the admin login is patched to use the application's login views to prevent circumvention of OTP. However, if you use third-party packages that register to the default django.contrib.admin.site, OTP might not be enforced on all admin views.

    To strictly enforce OTP for all admin pages, you should use a custom admin site. You can either use AdminSiteOTPRequired or AdminSiteOTPRequiredMixin.

    If you want to enforce OTP while continuing to use the default admin.site instance (to maintain compatibility with third-party packages), you can monkey patch the default AdminSite in your urls.py.

    from django.contrib import admin
    from two_factor.admin import AdminSiteOTPRequired
    
    # Monkey patch the default AdminSite in urls.py
    admin.site.__class__ = AdminSiteOTPRequired
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        ...
    ]
  7. Setup WebAuthn support

    master

    To enable WebAuthn device support:

    1. Install with WebAuthn extra

    $ pip install django-two-factor-auth[webauthn]

    2. Update INSTALLED_APPS

    INSTALLED_APPS = [
        ...
        'two_factor.plugins.webauthn',
    ]

    3. HTTPS Requirements

    WebAuthn requires your service to be reachable via HTTPS. An exception is made for localhost (which can use HTTP).

    If you are using a different domain and are behind a proxy, ensure you set SECURE_PROXY_SSL_HEADER in your Django settings:

    SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
  8. Configure django-two-factor-auth in Django settings

    master

    To set up the package, follow these configuration steps in your Django project:

    1. Update INSTALLED_APPS

    Add the required apps. Note that specific plugins (email, phonenumber, yubikey) are optional depending on the features you want to support.

    2. Configure MIDDLEWARE

    Add django_otp.middleware.OTPMiddleware. It must be placed after django.contrib.auth.middleware.AuthenticationMiddleware.

    3. Set Login URLs

    Update your settings.py to point to the new two-factor login pages.

    4. Update URL Configuration

    Include the two_factor.urls.urlpatterns in your project's root URL configuration.

    Warning: Remove any other existing login routes to prevent users from circumventing two-factor authentication. The Django admin interface is automatically patched to use the new method.

    INSTALLED_APPS = [
        ...
        'django_otp',
        'django_otp.plugins.otp_static',
        'django_otp.plugins.otp_totp',
        'django_otp.plugins.otp_email',  # <- for email capability.
        'otp_yubikey',  # <- for yubikey capability.
        'two_factor',
        'two_factor.plugins.phonenumber',  # <- for phone number capability.
        'two_factor.plugins.email',  # <- for email capability.
        'two_factor.plugins.yubikey',  # <- for yubikey capability.
    ]
    
    MIDDLEWARE = (
        ...
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django_otp.middleware.OTPMiddleware',
        ...
    )
    
    LOGIN_URL = 'two_factor:login'
    
    # this one is optional
    LOGIN_REDIRECT_URL = 'two_factor:profile'
    
    # In urls.py
    from two_factor.urls import urlpatterns as tf_urls
    urlpatterns = [
       path('', include(tf_urls)),
        ...
    ]
  9. Check compatibility requirements for django-two-factor-auth

    master

    Before installing django-two-factor-auth, ensure your environment meets the following dependency requirements:

    • Django: Currently supports Django 5.2 and 6.0.
    • Python: Supports Python 3.11, 3.12, 3.13, and 3.14 (subject to Django's own Python support limits).
    • django-otp: Requires version 0.8.x or higher.
    • django-formtools: Requires version 1.0.