Authenticate With Djoser
Tags: django, djoser, python, jwt
Published: 2021-05-24 10:27:30

**REST** implementation of Django authentication system. djoser library provides a set of Django Rest Framework views to handle basic actions such as registration, login, logout, password reset and account activation. It works with custom user model.

## Requirements:

- Django(Rest Framework)
- [MailHog](https://github.com/mailhog/MailHog) (email testing tool for developers)
- [Djoser](https://djoser.readthedocs.io/en/latest/getting_started.html)
- [DRF YASG](https://drf-yasg.readthedocs.io/en/stable/readme.html#usage)

## Project Setup:
create a virtual env.

    python -m venv env

    Activate
    ./env/Scripts/activate # for windows

    source env/bin/activate # linux user

Here i am using poetry you can use pip or pienv:
poetry Setup:

```
    poetry init
    poetry add django djangorestframework djoser djangorestframework_simplejwt django-cors-headers
    poetry add drf-yasg # for api docs
```

## Creating project and apps:
```
    django-admin startproject core
    python manage.py startapp accounts
```

## Configuring **settings.py**:

```
# Installed Apps

DJANGO_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]

PROJECT_APPS = ["accounts"]

THIRD_PARTY_APPS = [
"rest_framework",
"drf_yasg",
"djoser",
"corsheaders",
"rest_framework_simplejwt",
"rest_framework_simplejwt.token_blacklist",
]

INSTALLED_APPS = DJANGO_APPS + PROJECT_APPS + THIRD_PARTY_APPS

MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"corsheaders.middleware.CorsMiddleware", # middleware for cors-headers
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
```
## let's create a custom user model:
accounts/models.py

```
from django.db import models
from django.contrib.auth.models import (
    AbstractBaseUser,
    PermissionsMixin,
    BaseUserManager,
)


class CustomUserManager(BaseUserManager):
    def create_user(self, email, username, password=None, **extra_fields):
        if not email:
            raise ValueError("User must have an email")
        email = self.normalize_email(email)
        user = self.model(email=email, username=username, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, username, email, password=None, **extra_fields):
        user = self.create_user(username, email, password=password, **extra_fields)
        user.is_active = True
        user.is_staff = True
        user.is_admin = True
        user.save(using=self._db)
        return user


class CustomUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(max_length=255, unique=True)
    username = models.CharField(max_length=255, unique=True)
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    is_admin = models.BooleanField(default=False)

    objects = CustomUserManager()

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["username", "first_name", "last_name"]

    def get_full_name(self):
        return f"{self.first_name} - {self.last_name}"

    def get_short_name(self):
        return self.username

    def has_perm(self, perm, obj=None):
        return True

    def has_module_perms(self, app_label):
        return True

    def __str__(self):
        return self.email
```

##  [Customizing Serializers](https://github.com/sunscrapers/djoser/blob/master/djoser/serializers.py)
accounts/serializers.py
```
from djoser.serializers import UserCreateSerializer
from django.contrib.auth import get_user_model

User = get_user_model()


class UserCreateSerializer(UserCreateSerializer):
    class Meta(UserCreateSerializer.Meta):
        model = User
        fields = ("id", "email", "username", "first_name", "last_name", "password")

```

## Email setup: settings.py
```
# EMAIL CONFIG
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "localhost"
EMAIL_PORT = "1025"
EMAIL_HOST_USER = ""
EMAIL_HOST_PASSWORD = ""
EMAIL_USE_TLS = False
```
## Djoser JWT and CORS HEADERS: settings.py
[Note: you won't need cors header unless you use any frontend framework or libraries]
```
REST_FRAMEWORK = {
    "DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"],
    "DEFAULT_AUTHENTICATION_CLASSES": (
        "rest_framework_simplejwt.authentication.JWTAuthentication",
    ),
}


SIMPLE_JWT = {
    "AUTH_HEADER_TYPES": ("JWT",),
    "ACCESS_TOKEN_LIFETIME": timedelta(minutes=60),
    "REFRESH_TOKEN_LIFETIME": timedelta(days=1),
    "AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
}

# DJOSER CONFIG
DJOSER = {
    "LOGIN_FIELD": "email",
    "USER_CREATE_PASSWORD_RETYPE": True,
    "USERNAME_CHANGED_EMAIL_CONFIRMATION": True,
    "PASSWORD_CHANGED_EMAIL_CONFIRMATION": True,
    "SEND_CONFIRMATION_EMAIL": True,
    "SET_USERNAME_RETYPE": True,
    "SET_PASSWORD_RETYPE": True,
    "USERNAME_RESET_CONFIRM_URL": "password/reset/confirm/{uid}/{token}",
    "PASSWORD_RESET_CONFIRM_URL": "email/reset/confirm/{uid}/{token}",
    "ACTIVATION_URL": "activate/{uid}/{token}",
    "SEND_ACTIVATION_EMAIL": True,
    "SOCIAL_AUTH_TOKEN_STRATEGY": "djoser.social.token.jwt.TokenStrategy",
    "SOCIAL_AUTH_ALLOWED_REDIRECT_URIS": [
        "your redirect url",
        "your redirect url",
    ],
    "SERIALIZERS": {
        "user_create": "accounts.serializers.UserCreateSerializer",  # custom serializer
        "user": "djoser.serializers.UserSerializer",
        "current_user": "djoser.serializers.UserSerializer",
        "user_delete": "djoser.serializers.UserSerializer",
    },
}

# CORS HEADERS
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
```

## URL CONFIG:
- accounts/urls.py

```
from django.urls import path


urlpatterns = []

```
- main/urls.py

```
#main/urls.py

from django.contrib import admin
from django.urls import path
from django.urls.conf import include, re_path

# DRF YASG
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi

schema_view = get_schema_view(
    openapi.Info(
        title="Djoser API",
        default_version="v1",
        description="REST implementation of Django authentication system. djoser library provides a set of Django Rest Framework views to handle basic actions such as registration, login, logout, password reset and account activation. It works with custom user model.",
        contact=openapi.Contact(email="contact@snippets.local"),
        license=openapi.License(name="BSD License"),
    ),
    public=True,
    permission_classes=(permissions.AllowAny,),
)

urlpatterns = [
    path("admin/", admin.site.urls),
    re_path(
        r"^api/v1/docs/$",
        schema_view.with_ui("swagger", cache_timeout=0),
        name="schema-swagger-ui",
    ),
    path("api/v1/", include("accounts.urls")),
    path("api/v1/", include("djoser.urls")),
    path("api/v1/", include("djoser.urls.jwt")),
    
]

```
Here we:
1. include some url paths that djoser provides us.
2. and config for API docs
  http://127.0.0.1:8000/api/v1/docs/
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8no7n6fquw0cvybnp24e.png)
![Api docs](https://res.cloudinary.com/dazyxzm1e/image/upload/v1621847318/apidocs1_udpte8.png)

here are some urls:
- Register User
http://127.0.0.1:8000/api/v1/users/
  ![Register](https://res.cloudinary.com/dazyxzm1e/image/upload/v1621848797/register_r6b1rl.png)
- User activation
  http://127.0.0.1:8000/api/v1/users/activation/
  ![activation](https://res.cloudinary.com/dazyxzm1e/image/upload/v1621848177/useractivationMail_ffqkpk.png)

Requires two parameters:
  {
    "uid" :"some uid",
    "token": "some token"  
 }
  ![postman](https://res.cloudinary.com/dazyxzm1e/image/upload/v1621849039/userActivation_g1giul.png)
 Similarly you can work with below apis.
- User login
  http://127.0.0.1:8000/api/v1/jwt/create/
- Reset Password
  http://127.0.0.1:8000/api/v1/users/reset_password/
- Reset Password Confirm
  http://127.0.0.1:8000/api/v1/users/reset_password_confirm/
- User Me
  http://127.0.0.1:8000/api/v1/users/me/

we can work with social-auth too but we require extra configs.
here is the github link where i have work with [social auth](https://github.com/lyamaa/dj-auth-backend)

overall we are done. 😌😌😌😌
If you are stuck 👇👇👇. 
***
{% github https://github.com/lyamaa/dj_djoser no-readme %}
***