Building an authentication app with Django Authentication System | Python.

Photo by FLY:D on Unsplash

Building an authentication app with Django Authentication System | Python.

We can build an authentication for our projects using the Django authentication system.

Prerequisites:

  • Python
  • OOP
  • Django Basic knowledge(Django templates, views, URLs, settings)

This is what the Django documentation says about its authentication system:

Django’s authentication system in its default configuration. This configuration has evolved to serve the most common project needs, handling a reasonably wide range of tasks, and has a careful implementation of passwords and permissions. For projects where authentication needs differ from the default, Django supports extensive extension and customization of authentication.

Django authentication provides both authentication and authorization and is generally referred to as the authentication system, as these features are somewhat coupled.

We will use authentication views and built-in forms to create this app. The operations that this app will do are:

  • User Registration
  • Login
  • Logout
  • Password change

Setup

Let's start creating a folder for our project, then we create a virtual environment, and activate it:

mkdir django_authentication
cd django auth
py -m venv venv

cd venv/Scripts
activate

Now we install Django:

pip install django

We create a django project:

django_admin startproject django_authentication
cd django_authentication

Now we create our app:

py manage.py startapp user_auth

We have to add our app to django_authentication/settings.py and the URLs our app will redirect the users when they login or logout :

settings.py

LOGIN_REDIRECT_URL = '/home'
LOGOUT_REDIRECT_URL = '/home'

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'user_auth',
]

After we add our app to INSTALLED_APPS, then we run migrations using the following command in our terminal:

py manage.py migrate

According to the doc:

When django.contrib.auth is listed in your INSTALLED_APPS setting, it will ensure that four default permissions – add, change, delete, and view – are created for each Django model defined in one of your installed applications.

These permissions will be created when you run manage.py migrate; the first time you run migrate after adding django.contrib.auth to INSTALLED_APPS, the default permissions will be created for all previously-installed models, as well as for any new models being installed at that time. Afterward, it will create default permissions for new models each time you run manage.py migrate (the function that creates permissions is connected to the post_migrate signal).

We are going to implement Authentication Views to handle login, logout, and password management, we need to add django.contrib.auth.urls to django_authentication/urls.py

django_authentication/urls.py

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('users', include('django.contrib.auth.urls')),
]

This will include the following URL patterns:

users/login/ [name='login']
users/logout/ [name='logout']
users/password_change/ [name='password_change']
users/password_change/done/ [name='password_change_done']
users/password_reset/ [name='password_reset']
users/password_reset/done/ [name='password_reset_done']
users/reset/<uidb64>/<token>/ [name='password_reset_confirm']
users/reset/done/ [name='password_reset_complete']

We run the following command to run the server:

py manage.py runserver

In the user_auth directory, we create a folder to store our app's templates.

user_auth/
    __init__.py
    admin.py
    apps.py
    migrations/
    models.py
    templates/
        user_auth/
            home.html
            base.html
    tests.py
    urls.py
    views.py

Home

views.py

from django.shortcuts import render

# Create your views here.

def home_page(request):
    return render (request,"user_auth/home.html")

Here we just write a function that will render home.html

We have to create an urls.py file inside our app's directory to add the URL of the views we will create.

user_auth/urls.py

from django.contrib import admin
from django.urls import path, include
from . import views

urlpatterns = [
    path('', views.home_page, name="home"),

]

Now we have to include user_auth app URLs inside the project's urls.py file.

django_authentication/urls.py

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('users', include('django.contrib.auth.urls')),
    path('home/', include('user_auth.urls')),

]

We create two templates. base.html where we are going to add the HTML and CSS code that will be inherited by other HTML files. And home.html that which will be the template of the home page.

base.html


<!doctype html>
<html lang="en">


<div class="topnav">
    <a href="{% url 'home' %}">Home</a>
     {% if user.is_authenticated %}
        <a href="{% url 'logout' %}">Logout</a>
      {% else %}
            <a href="{% url 'login' %}">Login</a>
      {% endif %}  
    <a href="{% url 'signup' %}">SingUp</a>
  </div>

    <div class="container">
        {% block content %}
        {% endblock content %}

    </div>

</body>

<html>

<style type="text/css">

.topnav {
  background-color: #333;
  overflow: hidden;
  position: fixed; 
  top: 0; 
  width: 100%; 
}


.topnav a {
  float: left;
  color: #f2f2f2;
  text-align: center;
  padding: 14px 16px;
  text-decoration: none;
  font-size: 17px;
  font-family: sans-serif;
}

/* Change the color of links on hover */
.topnav a:hover {
  background-color: #ddd;
  color: black;
}


.topnav a.active {
  background-color: #04AA6D;
  color: white;
}

body {
    background-color: #DEB887;
}
</style>

home.html

{% extends 'user_auth/base.html' %}

{% block content %}

  <body>
      <div class= "greeting">
          <h1>Hello</h1>

        <p> Welcome {{ user.username|default:'' }}, to the Our site</p>
      </div>

  </body>

<style>

 .greeting {
  color: #F0FFFF;
  text-align: center;
  font-family: sans-serif;
 } 

</style>

{% endblock content %}

image.png

Inside templates/user_auth, we create a folder named "registration". We create in it a template for login, signup, and a form_base template. In the form_base template, we add the HTML and CSS that will be inherited by the login and signup templates.

form_base.html

The HTML and CSS were copied from this tutorial of Dennis Ivy.


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">

    <title>Django Auth</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300&display=swap" rel="stylesheet">

    <style>
        body {
            background-color: #DEB887;
            font-family: 'Poppins', sans-serif;
            padding-top: 50px;


        }
        h1,
        h2,
        h3,
        h4,
        h5,
        h6,
            {
            font-family:'Raleway', sans-serif;
        }

        a,
        p {
            color: #4b5156
        }


        .container{
            max-width: 550px;
            margin: auto;
            background-color: #FFEBCD;
            -webkit-box-shadow: 2px 2px 13px -4px rgba(0,0,0,0.21);
            box-shadow: 2px 2px 13px -4px rgba(0,0,0,0.21);

        }
        input {
            outline: none;
            border: none;
        }

        .header-bar {
            display:flex;
            justify-content:space-between;
            color:#fff;
            padding:10px;
            border-radius:5px 5px 0 0;
            background: #5F9EA0;
        }

        .header-bar a {
            color: rgb(247,247,247);
            text-decoration:none;

       }

        input[type=text],
        input[type=password],
        textarea {
            border: 1px solid #757575;
            border-radius: 5px;
            padding: 10px;
            width: 90%;
        }

        label {
            padding-top: 10px !important;
            display: block;
        }

        ::placeholder {
            font-weight: 300;
            opacity: 0.5;
        }

        .button {
            border: 1px solid #757575;
            background-color: #FFF;
            color: #EB796F;
            padding: 10px;
            font-size: 14px;
            border-radius: 5px;
            cursor: pointer;
            text-decoration: none;
        }

        .card-body {
            padding: 20px;
        }

        .topnav {
          background-color: #333;
          overflow: hidden;
          position: fixed; /* Set the navbar to fixed position */
          top: 0; /* Position the navbar at the top of the page */
          width: 100%; /* Full width */
        }

/* Style the links inside the navigation bar */
        .topnav a {
          float: left;
          color: #f2f2f2;
          text-align: center;
          padding: 14px 16px;
          text-decoration: none;
          font-size: 17px;
          font-family: sans-serif;
        }

/* Change the color of links on hover */
.topnav a:hover {
  background-color: #ddd;
  color: black;
}

    </style>

</head>
<body>
    <div class="topnav">
      <a href="{% url 'home' %}">Home</a>
       {% if user.is_authenticated %}
          <a href="{% url 'logout' %}">Logout</a>
        {% else %}
              <a href="{% url 'login' %}">Login</a>
        {% endif %}  
      <a href="{% url 'signup' %}">SingUp</a>
    </div>

    <div class="container">
        {% block content %}
        {% endblock content %}

    </div>

</body>
</html>

SignUp

Views.py

from django.shortcuts import render
from django.urls import reverse_lazy
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.views.generic.edit import CreateView

...

class SignUp(CreateView):
    form_class = UserCreationForm
    success_url = reverse_lazy("login")
    template_name = "user_auth/registration/signup.html"

We create a child class from CreateView. This class displays a form for creating an object, redisplaying the form with validation errors (if there are any), and saving the object. For this class, we give UserCreationForm as a value to form_class, this form creates a user, with no privileges, from the given username and password.

After the signup, the user will be taken to the login page. To template_name we give as a value the location of the template for this view.

signup.html

{% extends 'user_auth/registration/form_base.html' %}

{% block content %}
<div class="header-bar">
    <h1>Sign Up</h1>
</div>

<div class="card-body">
    <form method="POST">
        {% csrf_token %}
        <label>{{form.username.label}}</label>
        {{form.username}}

        <label>{{form.password1.label}}</label>
        {{form.password1}}

        <label>{{form.password2.label}}</label>
        {{form.password2}}
        <input style="margin-top:10px;" class="button" type="submit" value="Register">
    </form>
    <p>Already have an account?<a href="{% url 'login' %}">Login</a></p>
</div>
{% endblock %}

image.png

Login

from django.shortcuts import render
from django.urls import reverse_lazy
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.views.generic.edit import CreateView

...

class Login(LoginView):
    form_class = AuthenticationForm
    template_name = "user_auth/registration/login.html"

We create a child class from LoginView. And use AuthenticationForm as a form_class which is a form for logging a user in. Here we do not define success_url because it is already defined in settings.py.

According to the doc, this is what LoginView does:

Here’s what LoginView does:

If called via GET, it displays a login form that POSTs to the same URL. More on this in a bit.

If called via POST with user submitted credentials, it tries to log the user in. If login is successful, the view redirects to the URL specified in next. If next isn’t provided, it redirects to settings.LOGIN_REDIRECT_URL (which defaults to /accounts/profile/). If login isn’t successful, it redisplays the login form.

It’s your responsibility to provide the HTML for the login template , called registration/login.html by default. This template gets passed four template context variables:

form: A Form object representing the AuthenticationForm.

next: The URL to redirect to after successful login. This may contain a query string, too.

site: The current Site, according to the SITE_ID setting. If you don’t have the site framework installed, this will be set to an instance of RequestSite, which derives the site name and domain from the current HttpRequest.

site_name: An alias for site.name. If you don’t have the site framework installed, this will be set to the value of request.META['SERVER_NAME']. For more on sites, see The “sites” framework.

If you’d prefer not to call the template registration/login.html, you can pass the template_name parameter via the extra arguments to the as_view method in your URLconf. For example, this URLconf line would use myapp/login.html instead:

path('accounts/login/', auth_views.LoginView.as_view(template_name='myapp/login.html')),

login.html

{% extends 'user_auth/registration/form_base.html' %}

{% block content %}
  <div class="header-bar">
    <h1>Login</h1>
</div>

<div class="card-body">
    <form method="POST">
        {% csrf_token %}
        {{form.as_p}}
        <input class="button" type="submit" value="Login">
    </form>
    <p> Don't have an account?<a href="{% url 'signup' %}">Register</a></p>
</div>

{% endblock %}

image.png

Password Change

views.py

from django.shortcuts import render
from django.urls import reverse_lazy
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, PasswordChangeForm
from django.views.generic.edit import CreateView
from django.contrib.auth.views import LoginView, LogoutView, PasswordChangeView

...

class PasswordChange(PasswordChangeView):
    form_class = PasswordChangeForm
    template_name = "user_auth/registration/password_change_form.html"

The PasswordChangeView allows the user to change this password. The PasswordChangeForm is a form for allowing a user to change their password.

password_change.html

{% extends 'user_auth/registration/form_base.html' %}

{% block content %}
  <div class="header-bar">
    <h1>Password Change</h1>
</div>

<div class="card-body">
    <form method="POST">
        {% csrf_token %}
        {{form.as_p}}
        <input class="button" type="submit" value="Login">
    </form>
</div>

{% endblock %}

image.png

home.html

{% extends 'user_auth/base.html' %}

{% block content %}

  <body>
      <div class= "greeting">
        <h1> Welcome {{ user.username|default:'' }}, to the Django Auth site.</h1>
      {% if user.is_authenticated %}
        <p> Change you password<a href="{% url 'password_change' %}"> Here</a></p>
      {% endif %}    
      </div>

  </body>

<style>

 .greeting {

  color: #F0FFFF;
  text-align: center;
  font-family: sans-serif;
 } 

</style>

{% endblock content %}

image.png

Conclusion

Django allows us to create authentication for our projects very easy using its class base views. Also, we can implement a password reset with email using PasswordResetView and PasswordResetForm. This is an advantage because it allows us to invest more time in our app's business logic.

Thank you for taking the time to read this article.

If you have any recommendations about other packages, architectures, how to improve my code, my English, or anything; please leave a comment or contact me through Twitter, LinkedIn.

The source code is here.

References