Quickly add 2FA (email) for your custom Django admin
Tags: django
Published: 2024-04-19 05:27:32

---
title: Quickly add 2FA (email) for your custom Django admin
published: true
date: 2024-04-19 05:26:31 UTC
tags: Django
canonical_url: https://www.paleblueapps.com/rockandnull/django-custom-admin-2fa-security/
---

 ![Quickly add 2FA (email) for your custom Django admin](https://images.unsplash.com/photo-1485230405346-71acb9518d9c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wxMTc3M3wwfDF8c2VhcmNofDN8fHNlY3VyaXR5fGVufDB8fHx8MTcxMjkyNjA0Nnww&ixlib=rb-4.0.3&q=80&w=2000)

Keeping your Django admin panel safe is crucial. Regular passwords are good, but adding another layer of security is even better. That's where Two-Factor Authentication (2FA) comes in.

Just for clarity, I will repeat why 2FA is so important, even if it's a bit more inconvenient. If someone guesses/steals your password (the first lock), they still can't get in without the code from your authenticator app / email / hardware key (the second lock). This makes it much harder for attackers to break into your admin panel.

This post is the fastest guide for adding 2FA (with email) to your Django admin - especially if you're using a [custom admin interface](https://www.paleblueapps.com/rockandnull/django-admin-custom-view/) and the instructions in other blog posts are not working. I prefer email, and not an authenticator app since it's way easier to get started without requiring any additional new equipment/app.

## Get started

We will use the battle-tested [django-otp](https://github.com/django-otp/django-otp?ref=paleblueapps.com) project. The project supports many types of authenticators (such as authenticator apps, hardware keys, etc) so make sure to check out the documentation to add additional methods of authenticating if needed.

```
pip install django-otp
```

## Installation

Head to your `settings.py` and make the following modifications:

```
INSTALLED_APPS = [
    # [...]
    "django_otp",
    "django_otp.plugins.otp_static",
    "django_otp.plugins.otp_email",
]

MIDDLEWARE = [
    # [...]
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    # BELOW AuthenticationMiddleware!
    "django_otp.middleware.OTPMiddleware",
```

_settings.py_

Now somewhere, probably in `admin.py` you are defining your custom admin panel. Replace the base class with the OTP one (for the login screen to be replaced automatically with the one asking for the 2FA).

```
# REPLACE THIS
class CustomAdminSite(AdminSite):
    # [...]

# WITH THIS
class CustomAdminSite(OTPAdminSite):
    # [...]

admin_site = CustomAdminSite(name="myadmin")
```

_admin.py_

The final step, in your `urls.py` register the model for registering email as 2FAs for your users. **Important** to note here, that this registration should not be in the same file that you are defining the custom admin class since it will create circular dependencies.

```
# Placed here (and not in custom_admin/admin.py) to avoid circular import
admin_site.register(EmailDevice, EmailDeviceAdmin)
```

_urls.py_

Now run the migrations and you are ready to use it.

```
python manage.py migrate
```

Please note that the emails that will be sent, will be using [Django's email configuration](https://docs.djangoproject.com/en/dev/topics/email/?ref=paleblueapps.com#email-backends).

## Usage

When you go to your admin site, you will be greeted with 3 input fields: username/email, password, and OTP. To get access to actually set up 2FA for your users, you need a one-time password. This is what the `opt_static` app you installed above is doing. To generate a static one-time code run the following command (remember, you can only use once this code):

```
python manage.py addstatictoken my_username
```

Use this to log in, and head to `EmailOTP` model to set up the 2FA email for each user. Congratulations, your admin site is now protected with an additional authentication layer :)

Happy coding!