Users can opt-in to enhanced security by enabling two-factor authentication. There is currently no enforcement of a policy, it is entirely optional. However, you could override this behaviour to enforce a custom policy.

Limiting access to certain views

For increased security views can be limited to two-factor-enabled users. This allows you to secure certain parts of the website. Doing so requires a decorator, class mixin or a custom inspection of a user’s session.


You can use django-otp’s built-in otp_required() decorator to limit access to two-factor-enabled users:

from django_otp.decorators import otp_required

def my_view(request):


The mixin OTPRequiredMixin can be used to limit access to class-based views (CBVs):

class ExampleSecretView(OTPRequiredMixin, TemplateView):
    template_name = 'secret.html'


The method is_verified() is added through django-otp’s OTPMiddleware which can be used to check if the user was logged in using two-factor authentication:

def my_view(request):
    if request.user.is_verified():
        # user logged in using two-factor
        # user not logged in using two-factor

Enforcing two-factor

Forcing users to enable two-factor authentication is not implemented. However, you could create your own custom policy.

Admin Site

By default the admin login is patched to use the login views provided by this application. Patching the admin is required as users would otherwise be able to circumvent OTP verification. See also TWO_FACTOR_PATCH_ADMIN. Be aware that certain packages include their custom login views, for example django.contrib.admindocs. When using said packages, OTP verification can be circumvented. Thus however the normal admin login view is patched, OTP might not always be enforced on the admin views. See the next paragraph on how to do this.

In order to only allow verified users (enforce OTP) to access the admin pages, you have to use a custom admin site. You can either use AdminSiteOTPRequired or AdminSiteOTPRequiredMixin. See also the Django documentation on Hooking AdminSite instances into your URLconf.

If you want to enforce two factor authentication in the admin and use the default admin site (e.g. because 3rd party packages register to you can monkey patch the default AdminSite with this. In your

from django.contrib import admin
from two_factor.admin import AdminSiteOTPRequired = AdminSiteOTPRequired

urlpatterns = [


When a user was successfully verified using a OTP, the signal user_verified is sent. The signal includes the user, the device used and the request itself. You can use this signal for example to warn a user when one of his backup tokens was used:

from django.contrib.sites.shortcuts import get_current_site
from django.dispatch import receiver
from two_factor.signals import user_verified

def test_receiver(request, user, device, **kwargs):
    current_site = get_current_site(request)
    if == 'backup':
        message = 'Hi %(username)s,\n\n'\
                  'You\'ve verified yourself using a backup device '\
                  'on %(site_name)s. If this wasn\'t you, your '\
                  'account might have been compromised. You need to '\
                  'change your password at once, check your backup '\
                  'phone numbers and generate new backup tokens.'\
                  % {'username': user.get_username(),
        user.email_user(subject='Backup token used', message=message)

Show OTP Secret Key During Setup

Users who only have a smartphone will have difficulty scanning the QR code during setup. You can directly show the secret key within the QR code in text form during setup by providing your own two_factor/core/setup.html template and using the secret_key context variable.