{"id":3464,"library":"django-two-factor-auth","title":"Django Two-Factor Authentication","description":"Complete Two-Factor Authentication for Django. Built on top of the django-otp framework and Django's built-in authentication system, it offers easy integration into most Django projects. Inspired by Google's Two-Step Authentication, it supports various methods including token generator apps (like Google Authenticator), SMS, call, and YubiKey. The library is actively maintained with frequent releases, typically every few months.","status":"active","version":"1.18.1","language":"en","source_language":"en","source_url":"https://github.com/jazzband/django-two-factor-auth","tags":["django","authentication","two-factor","2fa","otp","security","mfa"],"install":[{"cmd":"pip install django-two-factor-auth","lang":"bash","label":"Base installation"},{"cmd":"pip install django-two-factor-auth[phonenumbers]","lang":"bash","label":"With phone number support"},{"cmd":"pip install django-two-factor-auth[webauthn]","lang":"bash","label":"With WebAuthn support"}],"dependencies":[{"reason":"Core one-time password framework.","package":"django-otp","optional":false},{"reason":"Used for generating QR codes for TOTP setup.","package":"qrcode","optional":false},{"reason":"Required for phone number capabilities (SMS, call).","package":"django-phonenumber-field","optional":true},{"reason":"Required by django-phonenumber-field.","package":"phonenumbers","optional":true},{"reason":"Alternative to phonenumbers, required by django-phonenumber-field.","package":"phonenumberslite","optional":true},{"reason":"For WebAuthn (FIDO2) support.","package":"webauthn","optional":true},{"reason":"For YubiKey hardware token support.","package":"django-otp-yubikey","optional":true},{"reason":"Optional, for generating PNG QR codes (default is SVG).","package":"Pillow","optional":true}],"imports":[{"note":"Imports the URL patterns for two-factor authentication.","symbol":"urlpatterns","correct":"from two_factor.urls import urlpatterns as tf_urls"},{"note":"The core OTP middleware comes from django_otp, not django-two-factor-auth directly.","wrong":"from two_factor.middleware import TwoFactorAuthMiddleware","symbol":"OTPMiddleware","correct":"from django_otp.middleware import OTPMiddleware"},{"note":"Mixin for Django AdminSite to enforce OTP.","symbol":"AdminSiteOTPRequiredMixin","correct":"from two_factor.admin import AdminSiteOTPRequiredMixin"}],"quickstart":{"code":"import os\n\n# settings.py\n# Add required apps, ensuring 'two_factor' is listed after django_otp plugins\nINSTALLED_APPS = [\n    'django.contrib.admin',\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n\n    'django_otp',\n    'django_otp.plugins.otp_static',\n    'django_otp.plugins.otp_totp',\n    # Optional plugins:\n    # 'django_otp.plugins.otp_email', # For email tokens\n    # 'two_factor.plugins.phonenumber', # For SMS/call tokens\n    # 'two_factor.plugins.email', # For email tokens (alternative)\n    # 'two_factor.plugins.yubikey', # For YubiKey support\n    # 'webauthn', # For WebAuthn support\n\n    'two_factor',\n]\n\n# Add OTP middleware after AuthenticationMiddleware\nMIDDLEWARE = [\n    'django.middleware.security.SecurityMiddleware',\n    'django.contrib.sessions.middleware.SessionMiddleware',\n    'django.middleware.common.CommonMiddleware',\n    'django.middleware.csrf.CsrfViewMiddleware',\n    'django.contrib.auth.middleware.AuthenticationMiddleware',\n    'django_otp.middleware.OTPMiddleware', # Must be after AuthenticationMiddleware\n    'django.contrib.messages.middleware.MessageMiddleware',\n    'django.middleware.clickjacking.XFrameOptionsMiddleware',\n]\n\n# Point to the two-factor authentication login/profile URLs\nLOGIN_URL = 'two_factor:login'\nLOGIN_REDIRECT_URL = 'two_factor:profile'\n\n# Optional: Configure email backend if using email tokens\nEMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'\nDEFAULT_FROM_EMAIL = os.environ.get('DEFAULT_FROM_EMAIL', 'webmaster@localhost')\n\n# urls.py\nfrom django.contrib import admin\nfrom django.urls import path, include\nfrom two_factor.urls import urlpatterns as tf_urls\n\nurlpatterns = [\n    path('admin/', admin.site.urls),\n    path('', include(tf_urls)), # Include two-factor URLs at the root or desired path\n    # path('account/', include(tf_urls)), # Alternative: include at a specific path\n]\n\n# To integrate with Django Admin (optional, and usually patched automatically)\n# from two_factor.admin import AdminSiteOTPRequiredMixin\n# class OTPAdminSite(AdminSiteOTPRequiredMixin, admin.AdminSite):\n#     pass\n# admin.site = OTPAdminSite() # Replace default admin site if needed, often not required.\n","lang":"python","description":"Install the library, add `django_otp` and `two_factor` (and any desired plugins) to `INSTALLED_APPS`, and ensure `OTPMiddleware` is in `MIDDLEWARE`. Then, configure `LOGIN_URL` and `LOGIN_REDIRECT_URL` to point to the library's views, and include its URL patterns in your project's `urls.py`. Remember to run `python manage.py migrate` after setup."},"warnings":[{"fix":"Add 'two_factor.plugins.phonenumber' to your `INSTALLED_APPS` setting.","message":"In version 1.14.0, the phone capability was moved to a separate plugin. If you used phone-based authentication, you must add 'two_factor.plugins.phonenumber' to your INSTALLED_APPS to restore this functionality.","severity":"breaking","affected_versions":">=1.14.0"},{"fix":"Ensure `OTPMiddleware` is correctly positioned after `AuthenticationMiddleware` in your `settings.py`.","message":"The `django_otp.middleware.OTPMiddleware` must be placed in `MIDDLEWARE` *after* `django.contrib.auth.middleware.AuthenticationMiddleware`. Incorrect ordering can lead to authentication issues.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Review the documentation for the specific authentication method and add the required plugin to `INSTALLED_APPS` and install extra packages.","message":"If using optional authentication methods (e.g., SMS, email, YubiKey, WebAuthn), ensure their respective plugins (e.g., `django_otp.plugins.otp_email`, `two_factor.plugins.phonenumber`) are correctly listed in `INSTALLED_APPS` and any necessary extra dependencies are installed.","severity":"gotcha","affected_versions":"All versions"},{"fix":"If `pydantic` is still needed for other parts of your project, install it directly (`pip install pydantic`).","message":"In version 1.16.0, the minimal `webauthn` dependency was upgraded to 2.0, which removed its `pydantic` dependency. If you were relying on `pydantic` through `webauthn`, you might need to add it as a direct dependency.","severity":"breaking","affected_versions":">=1.16.0"},{"fix":"Carefully review your project's `urls.py` and remove or comment out conflicting login URL patterns, especially `path('accounts/', include('django.contrib.auth.urls'))` if it's not being explicitly handled.","message":"To prevent circumvention of two-factor authentication, ensure that any other login routes in your project (e.g., `django.contrib.auth.urls`) are removed or disabled once `django-two-factor-auth` is configured as the primary login.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Always ensure relevant plugins (`django_otp.plugins.otp_yubikey`, `two_factor.plugins.email`, etc.) are in `INSTALLED_APPS`.","message":"Prior to 1.18.0, the documentation for setting up YubiKey support or email gateways occasionally missed informing users to add the corresponding `django-otp` or `two_factor` plugin to `INSTALLED_APPS`. This was fixed in docs, but the underlying common mistake remains.","severity":"gotcha","affected_versions":"<1.18.0 (docs), All versions (common mistake)"},{"fix":"Upgrade to `django-two-factor-auth` version 1.18.0 or newer if using `AdminSiteOTPRequiredMixin`.","message":"An infinite redirect issue with `AdminSiteOTPRequiredMixin` on the admin site was fixed in 1.18.0. If you are using this mixin, ensure you are on a compatible version to avoid login loops.","severity":"gotcha","affected_versions":"<1.18.0"}],"env_vars":null,"last_verified":"2026-04-11T00:00:00.000Z","next_check":"2026-07-10T00:00:00.000Z"}