One-time Passwords (OTP)

The otp extra provides support for one-time passwords (OTP) in HIdP. This allows users to enable two-factor authentication (2FA) for their accounts.

Note

OTP support in HIdP is based on django-otp.

Installation

OTP support in HIdP is included in the recommended extra. To install the recommended extra, use the following command:

pip install django-hidp[recommended]

See Installation Extras for more information on available extras.

Configuration

To enable OTP support in HIdP, the following needs to be added to the Django settings:

INSTALLED_APPS = [
    ...,
    'django_otp',
    'django_otp.plugins.otp_totp',
    'django_otp.plugins.otp_static',
    'hidp.otp',
    ...
]

MIDDLEWARE = [
    ...,
    # Add the OTP middleware to the middleware list, after 
    # the session and authentication middleware
    'django_otp.middleware.OTPMiddleware',
]

Important

The configuration above does not enable OTP verification on login just yet. Read the following sections to learn how to enable OTP verification for users.

Usage

After enabling OTP support in HIdP, users can enable two-factor authentication (2FA) for their accounts. Whether users are required to verify or enable 2FA can be configured by enabling one or more OTP Policies.

OTP Policies are used to configure the requirements for OTP verification. Site-wide policies are implemented as Django middleware, while view decorators can be used to apply policies to specific views.

Note

These sections use the same terminology as the Django-OTP documentation. For more details, refer to the Django-OTP glossary.

Available OTP Policies

Site-wide Policies

The following site-wide OTP policies are available. To enable a policy, add the middleware to the MIDDLEWARE list in the Django settings, after the django_otp.middleware.OTPMiddleware middleware.

hidp.otp.middleware.OTPRequiredMiddleware

Requires users to verify their OTP before accessing any view (that is not exempt using the otp_exempt decorator). This is the strictest policy: users must verify their OTP before they can access any part of the site, which also means that if they haven’t set up OTP yet, they will be redirected to the OTP setup view upon first login.

hidp.otp.middleware.OTPVerificationRequiredIfConfiguredMiddleware

Requires users to verify their OTP if they have configured OTP. This policy is less strict than OTPRequiredMiddleware because it allows users to access the site without verifying their OTP if they haven’t configured OTP yet.

hidp.otp.middleware.OTPSetupRequiredIfStaffUserMiddleware

Requires staff users to set up and verify their OTP before accessing any view. This policy is useful for enforcing OTP setup for staff users, while allowing non-staff users to access the site without setting up OTP.

Warning

This policy does not enforce other users that have OTP set up are required to verify their OTP. Also add OTPVerificationRequiredIfConfigured middleware in order to do so.

View Decorators

The following view decorators can be used to apply OTP policies to specific views:

django_otp.decorators.otp_required

Requires users to verify their OTP before accessing the view. See the Django-OTP documentation for more information.

hipd.otp.decorators.otp_exempt

Exempts users from verifying their OTP when accessing the view, even if a site-wide policy (middleware) requires OTP verification.

Examples

You can combine multiple policies by adding multiple middleware classes to the MIDDLEWARE list in the Django settings. For example, to make OTP required for staff users and optional for non-staff users, you can use the following configuration:

MIDDLEWARE = [
    ...,
    'django_otp.middleware.OTPMiddleware',
    'hidp.otp.middleware.OTPSetupRequiredIfStaffUserMiddleware',
    'hidp.otp.middleware.OTPVerificationRequiredIfConfiguredMiddleware',
    ...
]

If you have certain views that should be exempt from OTP verification, you can use the otp_exempt decorator:

from hidp.otp.decorators import otp_exempt

@otp_exempt
def my_view(request):
    # This view can be accessed without verifying OTP  
    ...


# Or, on a class-based view:
from django.utils.decorators import method_decorator
from django.views.generic import View

@method_decorator(otp_exempt, name='dispatch')
class MyView(View):
    ...

Customizing OTP Policies

You can create custom OTP policies by subclassing the hidp.otp.middleware.OTPMiddlewareBase class and implementing the user_needs_verification method. This method should return True if the user needs to verify their OTP, and False otherwise. The method may assume the user is authenticated and not yet verified.

For example, to create a policy that requires OTP verification for users with a specific group, you can create a custom middleware class like this:

from hidp.otp.middleware import OTPMiddlewareBase

class OTPRequiredForGroupMiddleware(OTPMiddlewareBase):
    def user_needs_verification(self, user):
        return user.groups.filter(name='RequireOTP').exists()

See the source code of the hidp.otp.middleware module for more examples of custom OTP policies.

class hidp.otp.middleware.OTPMiddlewareBase(*args, **kwargs)

Base class for OTP middleware.

This class provides a base implementation for OTP middleware. It provides a process_view method that checks whether a request needs to verify OTP and redirects to the OTP verification view if necessary. The conditions on when to require verification can be implemented by overriding the user_needs_verification method in a subclass. For more complex verification logic, you can override the request_needs_verification and view_func_needs_verification methods.

Views can be marked as exempt from OTP verification by using the otp_exempt decorator.

Middleware implementations should be placed after the authentication middleware and django_otp.middleware.OTPMiddleware. If request_needs_verification, it will redirect users to the OTP verification view if they have a configured OTP device, or else to the OTP setup view.

get_redirect_url(request)

Return the URL to redirect to when OTP verification is required.

If the user has an OTP device, they will be redirected to the OTP verification view. If they do not have an OTP device, they will be redirected to the OTP setup view.

Parameters:

request (HttpRequest) – The request object.

Returns:

The URL to redirect to.

Return type:

str

process_view(request, view_func, view_args, view_kwargs)

Process a view and check if OTP verification is required.

This method is called by the middleware to check if the user needs to verify their OTP. If verification is required, the user will be redirected to the OTP verification view, or to the OTP setup view if they do not have an OTP device.

Parameters:
  • request (HttpRequest) – The request object.

  • view_func (callable) – The view function that will be called.

  • view_args (list) – The positional arguments passed to the view function.

  • view_kwargs (dict) – The keyword arguments passed to the view function.

Returns:

HttpResponseRedirect if this policy requires the user to verify OTP, None otherwise.

Return type:

None or HttpResponseRedirect

request_needs_verification(request, view_func)

Check whether a request needs to verify OTP.

The request needs to verify OTP if the user needs verification and the view function needs verification. The user never needs to verify if they are not authenticated or already verified.

Parameters:
  • request (HttpRequest) – The request object.

  • view_func (callable) – The view function that will be called.

Returns:

Whether the request requires the user to verify OTP.

Return type:

bool

user_needs_verification(user)

Check if a user needs to verify their OTP.

By default, this will require all (authenticated and not verified) users to verify. Override this method to customize the verification logic.

Parameters:

user (User) – The user to check.

Returns:

Whether the user needs to verify their OTP.

Return type:

bool

view_func_needs_verification(view_func)

Check whether a view function needs to verify OTP.

Override this method if you need to customize the verification logic.

Parameters:

view_func (callable) – The view function to check.

Returns:

Whether the view function needs to verify OTP.

Return type:

bool