Implementation of authentication system on django

Introduction

Let me start with the question of why a user authentication system is needed at all?
It is important to have an authentication system on your website, if only because this way you will expand your network of clients/friends/partners/readers/customers (underline what is appropriate)
Let me make a reservation right away that the authentication system that you and I will write is not based on the built-in Django application, django.contrib.auth. This will be a separate application with a separate model for it. We will create a simple and understandable authorization system, based on anonymous browser session keys.
Let's get down to business.. Implementing user registration and authentication in Django is not difficult, although it requires specific skills. To begin with, we need a project we will implement user authentication system for .
Or a link to the repository on github
And if you still haven’t skipped to the finished solution, please follow me.

How session key authentication works

Now, so that you understand what I will write about , I need to explain how sessions generally work in django and how we can use them to create users. A session is a small file generated by the server that stores information about the user. This file remains on the server so that the server knows who it is communicating with. By user we mean the browser.
The process of communication between the user and the server
The browser sends a request and receives a cookie. The server communicates with the database
I talk more about sessions in django in the chapter setting up views in django

Setting up scripts and jQuery

Let's say you have your own website on which you need to implement an authentication system. To create a login page and a registration page. The question arises, where to start? I suggest starting with writing ajax requests.
But before that, let's create the appropriate directories for storing scripts and connect these scripts to our templates. Let's put you now in the Auth application folder. Then:

mkdir -p static/Auth/js;
cd static/Auth/js;
touch on_signup.js; touch on_signin.js;
	
In the signup.html template, insert it at the very end before {% endblock %}

			
<script src="{% static 'Auth/js/on_signup.js' %}"></script>
			
				
In the login.html template, do the same thing, just insert

			
<script src="{% static 'Auth/js/on_signin.js' %}"></script>
			
				
Now that everything you need has been pre-configured, you can move on to the essence or how to make this ajax request work.

Ajax request for registration

The most important thing is at the very beginning. Code of the previously connected script on_signup.js

const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;

function OnSignup(){
	var form_data = new FormData();
	form_data.append("csrfmiddlewaretoken", csrftoken);
	form_data.append("username", $("#username").val())
	form_data.append("email", $("#email").val())
	form_data.append("password",  $("#password").val())
	form_data.append("repassword", $("#repassword").val())
	$.ajax({
		type: "POST",
		url: "/signup/verify/",
		data: form_data,
		processData: false,
		contentType: false,
		headers: {'X-CSRFToken': csrftoken},
		mode: 'same-origin', // Do not send CSRF token to another domain.
		success: function(result) {
			$("#common-error").removeClass('error success')	
			$("#common-error").addClass('success')	
			$("#common-error").text(result.common)	
			setTimeout(function(){
				window.location.href = '/'
			},3000)
		},
		error: function(jqXHR, textStatus, errorThrown){
			$("#common-error").removeClass('error success')	
			$("#common-error").addClass('error')	
			$("#common-error").text(jqXHR.responseJSON.common)	
		}
	});
}

$(document).ready( function(){
	$("#signup").on('click',OnSignup)
})
				
Now let's deal with it in order.
What is csrftoken? CSRF token (Cross-Site Request Forgery) is a unique server-generated string. It allows you to protect yourself from hackers faking requests to the server for unauthorized access to site user resources.
In order for the form to be sent using the POST method, this token must be transferred to the server through a request.
Next, we collect data from the completed form and save it into a form object which we will send to the server.

var form_data = new FormData();
form_data.append("csrfmiddlewaretoken", csrftoken);
form_data.append("username", $("#username").val())
form_data.append("email", $("#email").val())
form_data.append("password",  $("#password").val())
form_data.append("repassword", $("#repassword").val())
				
We define the method for sending, the address at which we will contact the server. You need to remember it, because you will need to create the appropriate function to process this address.
Next, we define the behavior of our script. When the code returns 200, meaning the user has successfully registered, we will add a caption above the form and redirect him to the main page.

success: function(result) {
	$("#common-error").removeClass('error success')	
	$("#common-error").addClass('success')	
	$("#common-error").text(result.common)	
	setTimeout(function(){
	window.location.href = '/'
	},3000)
},
				
If it fails, that is, if the code is returned 406 (in my case), we will give a completely different message to the user. So that he corrects the identified errors.

error: function(jqXHR, textStatus, errorThrown){
	$("#common-error").removeClass('error success')	
	$("#common-error").addClass('error')	
	$("#common-error").text(jqXHR.responseJSON.common)	
}
				
Well, in the end, as I’m already used to, I set up the appropriate events. In this case, pressing the Singup button.

Ajax login request

It’s the same technique, only for confirmation, that is, entry to the site. And here a little less data will be sent than during registration

const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
function OnSignin(){
	var form_data = new FormData();
	form_data.append("csrfmiddlewaretoken", csrftoken);
	form_data.append("username", $("#username").val())
	form_data.append("password",  $("#password").val())
	$.ajax({
		type: "POST",
		url: "/login/verify/",
		data: form_data,
		processData: false,
		contentType: false,
		headers: {'X-CSRFToken': csrftoken},
		mode: 'same-origin',
		success: function(result) {
			$("#common-error").text(result.common)	
			setTimeout(function(){
				$("#common-error").removeClass('error success')	
				$("#common-error").addClass('error')	
				window.location.href = '/'
			},3000)
		},
		error: function(jqXHR, textStatus, errorThrown){
			$("#common-error").removeClass('error success')	
			$("#common-error").addClass('error')	
			$("#common-error").text(jqXHR.responseJSON.common)	
		}
	});
}

$(document).ready( function(){
	$("#signin").on('click',OnSignin)
})
				

Create and configure django user model

Now that you have working ajax requests, you need to make an appropriate model for the user. In the models.py file add:

from django.db import models
from django.template.defaultfilters import slugify
from django.urls import reverse


class Users(models.Model):
	name = models.CharField(max_length=256, unique=True)
	slug = models.SlugField(max_length=256, unique=True)
	email = models.EmailField(blank=False, unique=True)
	password = models.CharField(max_length=256, blank=False)

	def __str__(self):
	return self.name

	def get_absolute_url(self):
	return reverse(type, kwargs={"slug": self.slug})

	def save(self, *args, **kwargs):
	if not self.pk:
	# Newly created object, so set slug
	self.slug = slugify(self.name)

	super(Users, self).save(*args, **kwargs)
				
Don't forget to apply migrations to the database

./manage.py makemigrations
./manage.py migrate
				

Setting up views

Now you have a user model, scripts for sending requests to the server, templates, styles. After all, the site that based on django. What else do we need? I left the most important thing for later, namely validation, authentication and authorization of users. All this happens on the server. I added 3 more paths to urls.py in the Auth application. In total, your Auth/urls.py should look something like this:

from django.urls import path
from .views import login, signup, logout, login_verify, signup_verify

urlpatterns = [
	path('login/', login, name='login'),
	path('signup/', signup, name='signup'),
	path('logout/', logout, name='logout'),
	path('login/verify/', login_verify),
	path('signup/verify/', signup_verify),
]
				
Now let's look at each of them separately. Let's discuss all the nuances, so to speak.

Registration view

If we describe this function in general, then it is a set of a wide variety of checks. Such as password length, email address format compliance, and others. And if all checks are passed, a user is created.

def signup_verify(request):
	data = {
		'common': ' | '
	}
	status = 200
	if request.method == 'POST':
		regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b'
		username = request.POST['username']
		email = request.POST['email']
		password = request.POST['password']
		repeated_password = request.POST['repassword']
	if len(username) == 0:
		data['common'] += '⚠ user field is empty | '
		status = 406
	if len(email) == 0:
		data['common'] += '⚠ email is empty | '
		status = 406
	if len(password) == 0:
		data['common'] += '⚠ password is empty | '
		status = 406
	# Check if user already exist
	if Users.objects.filter(name=username).exists() or Users.objects.filter(slug=slugify(username)).exists():
		data['common'] += '⚠ user with such name already exist | '
		status = 406
	# Check if email already used
	if Users.objects.filter(email=email).exists():
		data['common'] += '⚠ this email already in use | '
		status = 406
	# Check if username's length is big enough
	if len(username) < 3:
		data['common'] += '⚠ too short user name | '
		status = 406
	# Check if username's length not to big
	if len(username) > 25:
		data['common'] += '⚠ too long user name | '
		status = 406
	# Check if password length is big enough
	if len(password) < 6:
		data['common'] += '⚠ too short password | '
		status = 406
	# Password does not match
	if password != repeated_password:
		data['common'] += '⚠ passwords mismatch | '
		status = 406
	# Email addres does not right
	if not re.fullmatch(regex, email):
		data['common'] += '⚠ email formant are not correct | '
		status = 406
	if status == 200:
		data['common'] = '✔ you have successfully sign up | '
		user = Users(name=username, email=email, password=password)
		user.save()
		return JsonResponse(data, status=status)

	else:
		status = 403
		data['common'] = "Allowed only POST request"
		return JsonResponse(data, status=status)
				

Login view

The login function is also a bunch of checks for the correctness of the entered data. Except that we create additional variables in the current session, such as is_auth and username. And if all the entered data is correct, the function will return the corresponding message and code.

def login_verify(request):
	data = {
	'common': ' | ',
	}
	status = 200
	if request.method == 'POST':
		username = request.POST['username']
		password = request.POST['password']
	# username,email,password fields does not filled up
	if len(username) == 0:
		data['common'] += '⚠ user name field is empty | '
		status = 406
	if len(password) == 0:
		data['common'] += '⚠ password field is empty | '
		status = 406

	# Check if user already exist
	required_user = Users.objects.filter(name=username)
	if required_user.exists():
		# Password does match
		if required_user.first().password == password:
			status = 200
			request.session["is_auth"] = True
			request.session["username"] = username
			data['common'] = '✔ You have successfully sign in, redirecting ... '
		else:
			data['common'] = '⚠ Wrong password'
			status = 406
	else:
		data['common'] = '⚠ No such user in database'
		status = 406

	return JsonResponse(data, status=status)
				

Logout view


def logout(request):
	request.session.flush()
	return HttpResponseRedirect("/")
				
Everything is quite simple here. We tell the server to clear (forget) about the current session with the user, and then redirect to the main one.

Authentication Use Case

Now that our site can register new users and detect the entry of existing ones, a question arises. What exactly should we do next? Firstly, I suggest changing the login label to logout. To do this, we modify our home page. In home.html Instead

			
<li>
<a class="nav__link nav__link_act" href="{% url 'login' %}">Login</a>
</li>
			
				
Than paste

			
{% if not request.session.is_auth %}
<li>
<a class="nav__link nav__link_act" href="{% url 'login' %}">Login</a>
</li>
{% else%}
<li>
<a class="nav__link nav__link_act" href="{% url 'logout' %}">Logout</a>
</li>
{% endif %}
			
				
Secondly, I propose to display the name and email of our user. Insert this piece of template after the last element of the list

			
{% if request.session.is_auth %}
<li>
<div class="nav__link" >{{ user.name }}</div>
<div class="nav__link" >{{ user.email }}</div>
</li>
{% endif %}
			
				
And in Main/views.py the home function will need to be improved

from django.shortcuts import render
from Auth.models import Users


def home(request):
	user = Users.objects.filter(name=request.session.get('username', 'Guest')).first()
	context = {
	'user': user,
	}
	return render(request, 'Main/home.html', context=context)

				
First we imported the Users model. Then we find the user using the value we saved earlier in the session,a username. We received it during logging, if you haven't forgotten yet.
After reloading the page and logging in, you will be redirected to the main page and see something like this testing website where displayed UI of authentication
This is the basic application of an authentication system. There's still a lot you can do with this. For example, create a separate page for each user or provide the ability to comment. But this is beyond the scope of this article. My goal was to show you how you can implement a user authentication system yourself in django, and it’s up to you to decide how to use it.

Conclusions

Now, you have everything you need to create your own user registration and authorization forms. It wasn't difficult, was it? Although, if you are not used to it, it may seem otherwise. To summarize.
I hope the time spent was well spent. And you learned something for yourself.

For those looking for a ready-made solution

Here is a link to download thefinished Django application.
Or a link to download the entire Django project.
Or if you don’t want to mindlessly download files from unknown sites, here is a link to the github repository.
If you decide to download the application, congratulations, you grasp the main feature of django, namely its modularity. This is how you can connect it to your site. Add the application to settings.py

INSTALLED_APPS = [ 
'Auth.apps.AuthConfig'
]
				
In urls.py at the project level, connect routes.

urlpatterns += (

path('', include('Auth.urls')),

)
				
Everything is ready and configured.

Materials used and useful resources


heart 2
3 connected dots 0