Django Part 3 - User authentication with dj-rest-auth and allauth
Published: 2022-06-03 09:49:57

For this part of the tutorial, we will be using the dj-rest-auth and allauth packages to help us create API endpoints for account registration, login and email verification. There are various different ways to implement user authentication with Django, will be using token authentication. 
We also need to install the Django rest framework package. While we are at it installing packages, we should get the django cors headers package too, as for best security practices we should be protecting against CORS attacks.

As usual, use pip to install the packages.

```
py -m  pip install dj-rest-auth django-allauth djangorestframework django-cors-headers
```

After installing the packages, we need to update the project settings. Add the following to the INSTALLED_APPS list in backend/settings.py.

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

Now we need to run some database migrations as the newly installed packages use some new database models.


```
py manage.py migrate
```

Add the cors middleware to the MIDDLEWARE list. It must be before the common middleware.

```
MIDDLEWARE = [
    ...
    'corsheaders.middleware.CorsMiddleware',
    ...
]

```

Add all of these settings to the bottom of settings.py.


```
REST_FRAMEWORK = {
    "DEFAULT_PERMISSION_CLASSES": [
        "rest_framework.permissions.AllowAny",
    ],
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework.authentication.SessionAuthentication",
        "rest_framework.authentication.TokenAuthentication",
    ],
}

AUTHENTICATION_BACKENDS = (
   "django.contrib.auth.backends.ModelBackend",
   "allauth.account.auth_backends.AuthenticationBackend"
)

CORS_ORIGIN_WHITELIST = (
    "http://localhost:3000",
    "http://localhost:8000",
)

CSRF_TRUSTED_ORIGINS = ["http://localhost:3000"]

SITE_ID = 1

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_USERNAME_REQUIRED = True
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_UNIQUE_EMAIL = True

ACCOUNT_CONFIRM_EMAIL_ON_GET = True
ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL = 'http://localhost:8000/api/v1/dj-rest-auth/login/'
LOGIN_URL = 'http://localhost:8000/api/v1/dj-rest-auth/login/'

```

For now, we are just using the console to receive the email verification email. If you want to use a real email, you can use Gmail by replacing the EMAIL_BACKEND setting with the following:

```
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = os.environ.get("EMAIL_USER")
EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_PASSWORD")
EMAIL_PORT = 587
```

It is best practice to use a .env file to store your email and password. Install the load dotenv package to use .env files.

```
py -m pip install python-dotenv
```

Now create a .env in the same directory as your settings file. In the .env file, write your email and password.


```
EMAIL_USER=”YOUR EMAIL"
EMAIL_PASSWORD="YOUR PASSWORD"
```

Add the following code to the top of the settings.py file.

```
from dotenv import load_dotenv
import os

load_dotenv()
```

If you try to use your regular email password, you will most likely get an error as you don’t have permission from Google. You will need to create an app password. You can find instructions on how to do that at https://support.google.com/mail/answer/185833?hl=en

Now we need to create the URLs to access the endpoints provided by the packages we installed.

In backend/urls.py add the following:

```
from django.contrib import admin
from django.urls import path, include, re_path
from dj_rest_auth.registration.views import VerifyEmailView, ConfirmEmailView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/dj-rest-auth/', include('dj_rest_auth.urls')),
    path(
        'api/v1/dj-rest-auth/registration/account-confirm-email/<str:key>/',
        ConfirmEmailView.as_view(),
    ), # Needs to be defined before the registration path
    path('api/v1/dj-rest-auth/registration/', include('dj_rest_auth.registration.urls')),
    path(
        'api/v1/dj-rest-auth/account-confirm-email/',
        VerifyEmailView.as_view(),
        name='account_email_verification_sent'
    ),
]
```

Next, we will create a serializer for the /user endpoint. Serializers in Django REST Framework are responsible for converting objects into data types understandable by javascript and front-end frameworks. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data. 

In the users directory, create a new file called serializers.py. In serializers.py:

```
from rest_framework import serializers
from .models import CustomUser

class UserProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = CustomUser
        fields = ('pk', 'email', 'username', 'email_verified')
```

Now we need to use the serializer by adding it to the settings.py file. At the bottom of settings.py:

```
REST_AUTH_SERIALIZERS = {
    'USER_DETAILS_SERIALIZER': 'users.serializers.UserProfileSerializer'
}
```

We have finally added everything we need. Navigate to http://127.0.0.1:8000/api/v1/dj-rest-auth/registration/ and try to create a new account.

Enter a username, email and password. You should receive a response containing an authentication key that will use for authenticated API requests. You should also receive an email to verify your email address. This will either be in your email inbox or the console depending on what you setup previously. Right now, following the link in the email will change a separate database model and not the email_verified field in our custom User model. 

We want to be able to update our user model, so we need to use a signal. This will send a signal when a user verifies their email. 
Create a signals.py file inside the users directory. In signals.py:

```
from allauth.account.signals import email_confirmed
from django.dispatch import receiver

@receiver(email_confirmed)
def email_confirmed_(request, email_address, **kwargs):
    user = email_address.user
    user.email_verified = True

    user.save()
```

Then, we need to edit the UsersConfig class in users/apps.py.

```
class UsersConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'users'

    def ready(self):
        import users.signals
```

Now follow the link you received to verify your email. Then login to the admin site and check the user model. Your newly created account should have it’s email_verified field set to True.

You can login to your new account at 
http://127.0.0.1:8000/api/v1/dj-rest-auth/login/

You can see the current logged in user data at http://127.0.0.1:8000/api/v1/dj-rest-auth/user/