Django REST framework JWT Authentication Sign up API with email confirmation.

Michal Dróżdż
7 min readJul 5, 2024

--

Photo by Christopher Gower on Unsplash

Getting started

This tutorial is an extension of the first part of JWT Authentication in the Django REST framework. In this section, we will focus on implementing user registration over a REST API with email account verification.

Step 1 — Install required packages

If you want to allow users to register via a REST API, dj-rest-auth provides a registration feature. It uses django-allauth , so we need to install it first and set it up.

Install requirements & basic setup

  1. Start by installing dj-rest-auth[with_social] , as it ensures django-allauth is installed.
# requirements.txt
Django==5.0.6
djangorestframework==3.15.2
djangorestframework-simplejwt==5.3.1 # JWT token implementation for DRF
dj-rest-auth[with_social]==6.0.0 # REST API authentication utils

2. Add django.contrib.sites, allauth, allauth.account, and dj_rest_auth.registration apps to INSTALLED_APPS in your django settings.py:

INSTALLED_APPS = [
...,
'django.contrib.sites',
...
'rest_framework',
'rest_framework.authtoken',
...
'allauth',
'allauth.account',
'allauth.socialaccount',
'dj_rest_auth.registration',
]

# django.contrib.sites
SITE_ID = 1

3. Add allauth.account.middleware.AccountMiddleware to MIDDLEWARE in settings.py

MIDDLEWARE = [
...
"allauth.account.middleware.AccountMiddleware",
]

4. Add django-allauth settings to Django’s settings.py. For now, we will disable the email verification feature and address it later.

ACCOUNT_AUTHENTICATION_METHOD = "email"  # Use Email / Password authentication
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_EMAIL_VERIFICATION = "none" # Do not require email confirmation

Full list of available settings: Django Allauth Configuration

5. Run Django migrations: python manage.py migrate

Step 2— Available endpoints

Expose the new registration endpoints in urls.py

urlpatterns = [
path("admin/", admin.site.urls),
path("api/v1/auth/", include("dj_rest_auth.urls")),
path('api/v1/auth/registration/', include('dj_rest_auth.registration.urls'))
...
]

Let’s take a look at the newly available endpoints:

/api/v1/auth/registration/      dj_rest_auth.registration.views.RegisterView    rest_register
/api/v1/auth/registration/account-confirm-email/<key>/ django.views.generic.base.TemplateView account_confirm_email
/api/v1/auth/registration/account-email-verification-sent/ django.views.generic.base.TemplateView account_email_verification_sent
/api/v1/auth/registration/resend-email/ dj_rest_auth.registration.views.ResendEmailVerificationView rest_resend_email
/api/v1/auth/registration/verify-email/ dj_rest_auth.registration.views.VerifyEmailView rest_verify_email

What we are looking for is the /api/v1/auth/registration/ ehe rest of the endpoints are used during email verification; however, this feature requires emailing to be enabled, so we’ll skip it for now by setting ACCOUNT_EMAIL_VERIFICATION = "none" in settings.py.

Step 3— Creating a new user

Registration API uses the RegistrationSerializer defined in dj-rest-auth's settings. By default, it uses dj_rest_auth.registration.serializers.RegisterSerializer to perform account creation.

Since we’ve set our authentication method to email via these three settings:

ACCOUNT_AUTHENTICATION_METHOD = "email"  # Use Email / Password authentication
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_EMAIL_REQUIRED = True

Call the API — you might need to logout first, from you current user, it order to create a new one. username field is now optional, if you won’t provide it, username will be generated based on your email address.

POST — /api/v1/auth/registration/ — Request

User was created successfully.

POST — /api/v1/auth/registration/ — HTTP 201 Response

Override default behaviour

If you want to modify the accepted parameters or perform any action on user creation, you can use a custom RegisterSerializer by specifying it in the settings.

REST_AUTH = {
...
"REGISTER_SERIALIZER": "myapp.serializers.CustomRegisterSerializer",
}

Full list of dj-rest-auth’s settings

Step 4— Email verification (optional)

If you want to enforce email verification for your users, you need to set up email sending in Django first. This can be achieved by using libraries like django-anymail, which supports multiple email service providers, django-ses, which brings an AWS SES email backend to Django.

Gmail SMTP
Using a reliable third-party ESP is recommended, but for the sake of this tutorial, we can also use Gmail’s SMTP service. If you have a Google account, you can use generated app credentials to authenticate against the Google SMTP server to send out emails.

  1. Go to your Gmail account settings.

2. Enable 2-Step Verification.

3. Type ‘App passwords’ in the search bar above and select it.

4. Copy the generated App password — you can see it only once.

Django settings

First, we need to add some Django settings to set up email sending. These settings come directly from Django and do not require any additional packages to be installed.

# Django SMTP
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.gmail.com"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = "<your-email-address>" # email sending address
EMAIL_HOST_PASSWORD = "<app-password>"

Now, let’s tell dj-rest-auth along with django-allauth that users should confirm their emails before signing in.

ACCOUNT_EMAIL_VERIFICATION = "mandatory" # Require email confirmation
ACCOUNT_CONFIRM_EMAIL_ON_GET = True # No need to sent POST request to confirmation link
LOGIN_URL = "/admin" # Path, users will be redirected to after email verification

You can apply further customization with: ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL

Test the integration

POST — /api/v1/auth/registration/ — Request

A confirmation email was sent; we need to confirm it before we are allowed to sign in.

POST — /api/v1/auth/registration/ — HTTP 201 Response

Go to your inbox and click the verification link.

Email confirmation message

If you click this link, you will probably get a TemplateResponseMixin exception.

TemplateResponseMixin requires either a definition of 'template_name' or an implementation of 'get_template_names()'

That’s because, if you take a look at the endpoints we checked earlier, account-confirm-emailendpoint points to a placeholder TemplateView.

/api/v1/auth/registration/account-confirm-email/<key>/  django.views.generic.base.TemplateView  account_confirm_email

Manually add the ConfirmView to your URLs. Make sure you add it before the generic TemplateView definition.

from allauth.account.views import ConfirmEmailView

urlpatterns = [
...
re_path(
"^api/v1/auth/registration/account-confirm-email/(?P<key>[-:\w]+)/$",
ConfirmEmailView.as_view(),
name="account_confirm_email",
),
path('api/v1/auth/registration/', include('dj_rest_auth.registration.urls')),
]

Click the link again and you should be redirected to your admin page. Or any page that you set as: LOGIN_URL

If you need more customisation, you can set additional redirect rules in the settings.

import os

# The URL to redirect to after a successful email confirmation, in case no user is logged in.
ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = os.environ.get(
"EMAIL_CONFIRM_REDIRECT_URL", "https://myapp.com/login"
)
# The URL to redirect to after a successful email confirmation, in case of an authenticated user.
ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL = os.environ.get(
"EMAIL_CONFIRM_REDIRECT_URL", "https://myapp.com/login"
)

How to change the website domain in email messages?

The default example.com is the name of the Django Site created by default. You can edit its name via the Django Admin.

How to change the email verification template to send custom emails?

You can override the template(s) by placing a file with the correct name in your Django project.

Tell Django it should look for templates in one extra directory. We will populate it in a moment.

TEMPLATES = [
{
...
'DIRS': [BASE_DIR / 'templates'],
...
},
]

Start by creating a few nested directories: /templates/account/email, in the root of your project.
Now you can create three files to override the HTML content, plain text content, and the email subject.

  • email_confirmation_signup_message.txt
Hello {{ user.username }},

This is a custom message, please click the link below to confirm you email address:

[Confirm Email: {{ activate_url }}]

Best, Michał
  • email_confirmation_signup_message.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DRF JWT Tutorial</title>
</head>
<body>
<center class="wrapper">
<table class="main" width="100%">
<tr>
<td style="padding: 20px">
<div>
<h3>Hello {{ user.username }}</h3>
<p>
This is a custom message, please click the link below to confirm you email address:
</p>
<p>
<a href="{{ activate_url }}">Confirm Email</a>
</p>
<p>
Best, Michał
</p>
</div>
</td>
</tr>
</table>
</center>
</body>
</html>
  • email_confirmation_signup_subject.txt
Confirm your drf-jwt-tutorial account!

Test the email again.

Email confirmation message

Conclusion

In this tutorial, we installed dj-rest-auth[with_social], which usesdjango-allauth underneath to handle user management. We exposed a user registration API and tested it. Lastly, we set up an email sending service and set email verification as mandatory during sign-up.

Code Repository

Example code from this tutorial is available in my GitHub repository:
https://github.com/Mich0232/drf-jwt-register-tutorial

Further reading
Please make sure you check out the other parts of this tutorial:

Thank you for reading!

--

--