A lightweight middleware application that adds a layer on top of any number of existing authentication backends, enabling 2FA with the user's Duo account using the Universal Prompt after signing in with your Django application.
Note: In order to interface this middleware with Duo, you must create a new Duo Web SDK application from within your organization's Duo Admin Portal and enable the "Show new Universal Prompt" setting. You will acquire a Client ID, Client Secret, and API Hostname, of which you will include in your settings.py
file in the format listed below. It is strongly recommended not to hardcode these values in the settings file itself.
From Duo's documentation for protecting applications:
Treat your Secret key or Client ID like a password The security of your Duo application is tied to the security of your Secret key (skey) or Client secret (client_secret). Secure it as you would any sensitive credential. Don't share it with unauthorized individuals or email it to anyone under any circumstances!
To install the middleware application, use the following pip
command (or equivalent for your package manager application):
pip install django-duo-universal-auth
First, add the package to your INSTALLED_APPS
list variable:
INSTALLED_APPS = [
# ...
'duo_universal_auth', # Add this!
]
Next, add the path for the middleware application to the MIDDLEWARE
list variable:
MIDDLEWARE = [
# ...
'duo_universal_auth.middleware.DuoUniversalAuthMiddleware', # Add this!
]
Then, add a new DUO_UNIVERSAL_AUTH
configuration variable:
DUO_UNIVERSAL_AUTH = {
'MAIN': {
'DUO_HOST': '<api_hostname>',
'CLIENT_ID': '<client_id>',
'CLIENT_SECRET': '<client_secret>',
'AUTH_BACKENDS': [
'django.contrib.auth.backends.ModelBackend',
],
'FAIL_ACTION': 'CLOSED'
}
}
Note: This step allows the application to communicate with Duo. If the view is not registered, the application will raise a NoReverseMatch
error upon starting the Duo authentication flow.
To create the callback for the API to communicate with, you must add an entry to your urlpatterns
variable from within your application's urls.py
file (with any prepending path you choose):
from django.urls import path, include
urlpatterns = [
# ...
path('duo/', include('duo_universal_auth.urls')), # Add this!
]
Configurations for each Duo application are specified within individual dictionary objects inside a parent DUO_UNIVERSAL_AUTH
dictionary each containing the following values:
Represents the API Hostname for your organization's Duo API.
'DUO_HOST': 'api-XXXXXXX.duosecurity.com'
Represents the Client ID for your application registered from within the Duo Admin Portal.
'CLIENT_ID': 'DIXXXXXXXXXXXXXXXXXX'
Represents the Client Secret for your application registered from within the Duo Admin Portal.
'CLIENT_SECRET': 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeef'
A list of authentication backends that the middleware will work with for the specific application. The Duo authentication middleware will only execute upon a successful authentication result from one of these backends.
'AUTH_BACKENDS': [
'django.contrib.auth.backends.ModelBackend',
]
How the middleware should respond should the Duo authentication server be unavailable (from failing the preliminary health check).
'CLOSED'
: Log out the user and return to the login page, disallowing any authentication while Duo servers are unavailable.'OPEN'
: Temporarily bypass Duo authentication until the Duo servers become available upon a future authentication attempt.
'FAIL_ACTION': 'CLOSED'
An optional one-argument function that takes in the current Django HttpRequest
object and returns the current authenticated user's username to send for Duo authentication. If unspecified, the username from HttpRequest.user
will be used.
'USERNAME_REMAP_FUNCTION': lambda r: r.user.username # Mimics default behavior
Once successfully authenticated with Duo, the middleware will automatically redirect the user to the path specified in the DUO_NEXT_URL
session variable, falling back to the LOGIN_REDIRECT_URL
settings variable if it is not present. This value is automatically assigned in the middleware before redirecting to Duo.