User creation using Django AbstractBaseUser and Login using JWT web authentication

Fahim Ahmed Irfan
6 min readJun 3, 2021

In this article, I will try to cover Django user creation, defining a custom user mode extending AbstractBaseUser class and JWT web authentication for user Login.

I will use Django Rest Framework for making sign up API and Login API will be provided by JWT web authentication.

First, we need to create a project using django-admin startproject command:

django-admin startproject custom_auth

Then we need to activate virtual environment and install the necessary packages for completing this project. We will install all the packages using pip. Run these commands one by one:

pip install django
pip install postgres
pip install djangorestframework
pip install djangorestframework-simplejwt

Now we need to modify our project settings.py file to configure the project.

Configure database section:

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': '',
'USER': '',
'PASSWORD': '',
'HOST': 'localhost',
'PORT': '5432',
}
}

Please provide the preferred name, user, and password.

Modify installed apps:

INSTALLED_APPS = [
.....
.....
'rest_framework',
'rest_framework_simplejwt',
.....
.....
]

Add rest framework configs:

# Rest framework
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),
'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework_simplejwt.authentication.JWTAuthentication',)
}

Now need to migrate database:

python manage.py makemigrations
python manage.py migrate

If these two commands run without any error, this means the project is ready.

After that, we need to go to the project directory, in this case, the directory is custom_auth, here we will run startapp command for creating our apps.

I will create a core app. Normally I keep all the model classes in one models.py and that is in the core app.

Create app:

python manage.py startapp core

Now register this app to settings.py INSTALLED_APP section

Now we need to write custom user model class in models.py. First, we need to create a manager class extending from BaseUserManager. This class mainly manages user creation for the different privileged users.

class UserProfileManager(BaseUserManager):
"""
Defines user creation fields and manages to save user
"""
def create_user(self, email, username, password=None):
if not email:
raise ValueError('Users must have an email address')
user = self.model(email=self.normalize_email(email),
username=username,
)
user.set_password(password)
user.save(using=self._db)

return user
def create_staffuser(self, email,username, password=None):
user = self.create_user(email,
password=password,
username=username
)
user.is_staff = True
user.save(using=self._db)

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

return user

Now we need to create custom user model extending AbstractBaseUser. This class saves user information to database.

class UserProfile(AbstractBaseUser):
"""
Creates a customized database table for user using customized user manager
"""
email = models.EmailField(verbose_name='email address',
max_length=255,unique=True,
)
username = models.CharField(_('username'),
max_length=150,
unique=True,
error_messages={
'unique': _("A user with that username already exists."),
},
)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)

USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email',]
objects = UserProfileManager()def get_full_name(self):
return self.email
def get_short_name(self):
return self.email
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True

After that, we need to tell django that we are using custom user model. For this to settings.py:

AUTH_USER_MODEL = 'core.UserProfile'

We need to register this model to core app’s admin.py also. In admin.py:

from django.contrib.auth import get_user_modelUserProfile = get_user_model()
admin.site.register(UserProfile)

Now migrate these models. Once migration is done, we will create superuser:

python manage.py createsuperuser

After that, we will run our local server:

python manage.py runserver

Now, we will visit Django administration to see our registered custom user model

User Profiles registered in Django admin

So, if we can reach here successfully, we can say we have done our half works. Now we need to create sign up API and login API. First, we need to setup rest framework in settings.py (if not we set it up earlier):

# Rest frameworkREST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),
'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework_simplejwt.authentication.JWTAuthentication',)
}

We need to create an app called authentication. Here, we write our APIs for sign up and login. We need to set up our authentication app’s url and need to register the urls to project’s urls.py

urlpatterns = [
path('admin/', admin.site.urls),
path('auth/', include('authentication.urls',
namespace='authentication'))
]

First, we need to define our sign up fields in ModelSerializer. Create a serializers.py in authentication app.

from core.models import UserProfile
from rest_framework import fields, serializers
from django.contrib.auth.hashers import make_password
from django.contrib.auth.password_validation import validate_password
class SignupSerializer(serializers.ModelSerializer):
"""
Serializer for creating new user using signup form
"""
class Meta:
model = UserProfile
fields = ['id', 'username', 'email', 'password']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
if validate_password(validated_data['password']) == None:
password = make_password(validated_data['password'])
user = UserProfile.objects.create(
username=validated_data['username'],
email=validated_data['email'],
password=password
)
return user

In this serializer model, we validate our sign up data. If everything is fine, the model serializer will make the data ready to be saved.

Now, we will create our sign up APIView in views.py:

from core.models import UserProfile
from rest_framework.exceptions import ValidationError
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from . serializers import SignupSerializer
# Create your views here.class SignupAPIView(APIView):
"""
API view for sign up API
"""
permission_classes = [] def post(self, request):
password = request.POST.get('password', None)
confirm_password = request.POST.get('confirm_password', None)
if password == confirm_password:
serializer = SignupSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
data = serializer.data
response = status.HTTP_201_CREATED
else:
data = ''
raise ValidationError(
{'password_mismatch': 'Password fields didn not match.'})
return Response(data, status=response)

This view will post sign up data to database. Now we need to set API endpoint in urls.py:

from django.urls import path
from .views import SignupAPIView
app_name = 'authentication'urlpatterns = [
path('signup/', SignupAPIView.as_view(), name='signup')
]

Now we need to test the API. For testing purposes, we will postman. It is a very useful tool for testing API. In postman it will look like the image:

Sign up API in postman

Make sure the keys are similar to serializers fields. If not, then it may cause some error.

Now the final part. In this part we need to create login API. For that, we need setup JWT authentication in settings.py:

# JWTSIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': datetime.timedelta(minutes=30),
'REFRESH_TOKEN_LIFETIME': datetime.timedelta(days=1),
}

Access token authenticates a user and refresh token is used for generating access token once access token’s lifetime is over. Now we need to setup the endpoints for generating this tokens. In authentication app’s url.py:

from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView, TokenVerifyView
# JWT
urlpatterns = [
path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),path('token/verify/', TokenVerifyView.as_view(), name='token_verify'),
]

TokenObtainPairView will create access token and refresh and TokenRefreshView will create access token from refresh token.

Now we need to test this APIs in postman:

Generates access and refresh token using token/ API
Generates access token using /token/refresh/ API

So after all these hard works, finally we are able to use custom user model to create new user and JWT web authentication to authenticate the users.

Find the whole project in github: https://github.com/Irfan995/django-custom-auth

For more details, must see the official documentation:

  1. https://docs.djangoproject.com/en/3.2/topics/auth/customizing/
  2. https://www.django-rest-framework.org/
  3. https://django-rest-framework-simplejwt.readthedocs.io/en/latest/

--

--