Django — default (built-in) user authentication system

Dec. 29, 2020

  • server1.jpg Back-End

Initializing and starting python virtual environment:

python -m venv env
source env/bin/activate

Installing python packages:

pip install Django

Creating project: instead of known command

django-admin startproject default_auth

which generates the structure:

default_auth/
├── default_auth
│   ├── asgi.py
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.py

we could use, alternatively:

mkdir default_auth
cd default_auth
django-admin startproject src .

which generates structure:

default_auth
├── manage.py
└── src
    ├── asgi.py
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

Django, automatically installed ‘django.contrib.auth’ modules in /src/settings.py which will perform all the ‘magick’ af user authentication.

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

To use ‘auth’ we need to activate predefined urls of this package in /src/urls.py

from django.contrib import admin
from django.urls import path, include  #add this

urlpatterns = [
    path('admin/', admin.site.urls),
    path('accounts/', include('django.contrib.auth.urls')), #add this
]

After:

python manage.py migrate
python manage.py runserver

and accesing

http://localhost:8000/accounts/

django gives as a ‘Page not found (404)’ in which we could see listed the urls provided by ‘auth’ module:

admin/
    accounts/ login/ [name=’login’]
    accounts/ logout/ [name=’logout’]
    accounts/ password_change/ [name=’password_change’]
    accounts/ password_change/done/ [name=’password_change_done’]
    accounts/ password_reset/ [name=’password_reset’]
    accounts/ password_reset/done/ [name=’password_reset_done’]
    accounts/ reset/<uidb64>/<token>/ [name=’password_reset_confirm’]
    accounts/ reset/done/ [name=’password_reset_complete’]

Django ‘auth’ application has built-in url and views for login and logout so, all we need is to create the templates for login and logout procedures:

mkdir templates
mkdir templates/registration

Update /src/settings.py

TEMPLATES = [
    {
        ...
        'DIRS': [str(BASE_DIR.joinpath('templates'))],
        ...
    },
]

Create ‘/templates/base.html’ (using Bootstrap 4 starter template):

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

    <title>{% block title %}Django Auth Tutorial{% endblock %}</title>
  </head>
  <body>

    <div class="container">
        <main>
        {% block content %}
        {% endblock %}
        </main>
    </div>
    
    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
  </body>
</html>

Login and logout

We need to create login and logout templates.

First, create an accounts app

python manage.py startapp accounts
Update '/src/urls.py'
urlpatterns = [
    path('admin/', admin.site.urls),
    path('accounts/', include('django.contrib.auth.urls')),
    path('', include('accounts.urls')),  #add this
]

Create '/accounts/urls.py'

from django.urls import path
from . import viewsurlpatterns = [
    path('', views.home, name = 'home'),
]

Now, we are able to access home page at ‘http://localhost:8000/’ which instructs us to login.

Designing a login template:

/templates/registration/login.html

{% extends 'base.html' %}

{% block content %}
<h2>Log In</h2>
<form method="post">
  {% csrf_token %}
  {{ form.as_p }}
  <button type="submit">Log In</button>
</form>
{% endblock %}

After login we need a home redirect home page.

‘/accounts/views.py’

from django.shortcuts import render

def home(request):
    return render(request, 'home.html', {})

Add to ‘/src/settings.py’ to redirect to home.html page after login:

LOGIN_REDIRECT_URL = '/'

create ‘/templates/home.html’

{% extends 'base.html' %}

{% block title %}Home{% endblock %}

{% block content %}

{% if user.is_authenticated %}
  Hi {{ user.username }}!
  <p><a href="{% url 'logout' %}">Log Out</a></p>
  
{% else %}
  <p>You are not logged in</p>
  <p><a href="{% url 'login' %}">Log In</a></p>
  
{% endif %}

<p>Messages:</p>
{%if messages%}
    {%for message in messages%}
        {{message}}
    {%endfor%}
{%endif%}

As we don’t have any user, for simplicity, we could use a superuser first which we define with:

python manage.py createsuperuser

or, we can create users in admin inteface.

After logout we are redirected to an ugly admin template so we also need a redirect to home.html instruction in /src/settings.py

LOGOUT_REDIRECT_URL = '/'

Now, we have a login/logout authentication system working. We need some design improvement for the login template with Django Crispy Forms:

pip install django-crispy-forms

Add ‘crispy_forms’ to INSTALLED_APPS in ‘/src/settings’.

Update’ /templates/registration/login.html’

{% extends 'base.html' %}

{% load crispy_forms_tags %}

{% block title %}Login{% endblock %}

{% block content %}
<h2>Log In</h2>
<form method="post">
  {% csrf_token %}
  {% comment %}{{ form.as_p }}{% endcomment %}
  {{ form|crispy }}
  <button type="submit">Log In</button>
</form>
{% endblock %}

Now, username and password fields from login.html form have equal width.

Signup

For a signup page we need to create our own view and url.

Add ‘accounts’ to INSTALLED_APPS from ‘/src/settings’

Update ‘/accounts/urls.py’

from django.urls import path

from . import viewsurlpatterns = [
    path('', views.home, name = 'home'),
    path('accounts/signup/', views.signup, name='signup'), #add this
]

Update ‘/accounts/views.py’ with:

from django.urls import reverse
from django.shortcuts import redirect
from django.contrib.auth.forms import UserCreationForm

def signup(request):

    if request.method == 'POST':
        form = UserCreationForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect(reverse("login"))
        else:
            return redirect(reverse("home"))    

    form = UserCreationForm()
    return render(request, "registration/signup.html", {"form":form})

create ’/templates/registration/signup.html’

{% extends 'base.html' %}

{% load crispy_forms_tags %}

{% block title %}Sign Up{% endblock %}

{% block content %}
  <h2>Sign up</h2>
  <form method="post">
    {% csrf_token %}
    {% comment %}{{ form.as_p }}{% endcomment %}
    {{ form|crispy }}
    <button type="submit">Sign Up</button>
  </form>
{% endblock %}

Password reset

First of all we need to activate the Django development mail service (Django testing SMTP service) and the testing directory where the reset email password will be deployed. Add this in /src/settings.y

#fake mail deliver
EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
EMAIL_FILE_PATH = str(BASE_DIR.joinpath('sent_emails'))

The default reset templates are not very pretty so ve need to redesign them.

Cretate ‘/templates/registration/password_reset_form.html’

{% extends 'base.html' %}

{% load crispy_forms_tags %}

{% block title %}Forgot Your Password?{% endblock %}

{% block content %}
  <h1>Forgot your password?</h1>
  <p>Enter your email address below, and we'll email instructions for setting a new one.</p>

  <form method="POST">
    {% csrf_token %}
    {% comment %}{{ form.as_p }}{% endcomment %}
    {{ form|crispy }}
    <input type="submit" value="Send me instructions!">
  </form>
{% endblock %}

‘/templates/registration/password_reset_done.html’

{% extends 'base.html' %}

{% block title %}Email Sent{% endblock %}

{% block content %}
  <h1>Check your inbox.</h1>
  <p>We've emailed you instructions for setting your password. You should receive the email shortly!</p>
{% endblock %}

‘templates/registration/password_reset_complete.html’

<!-- templates/registration/password_reset_complete.html -->
{% extends 'base.html' %}

{% block title %}Password reset complete{% endblock %}

{% block content %}
<h1>Password reset complete</h1>
<p>Your new password has been set. You can log in now on the <a href="{% url 'login' %}">log in page</a>.</p>
{% endblock %}

Finnaly, update /templates/home.html

{% extends 'base.html' %}

{% block title %}Home{% endblock %}

{% block content %}

{% if user.is_authenticated %}
  Hi {{ user.username }}!
  <p><a href="{% url 'logout' %}">Log Out</a></p>
  <p><a href="{% url 'password_reset' %}">Reset Password</a></p>
{% else %}
  <p>You are not logged in</p>
  <p><a href="{% url 'login' %}">Log In</a></p>
  <p><a href="{% url 'signup' %}">Sign Up</a></p>
{% endif %}

<p>Messages:</p>
{%if messages%}
    {%for message in messages%}
        {{message}}
    {%endfor%}
{%endif%}

When accesing ‘http://localhost:8000/accounts/password_reset/’, be sure that there are users that have emails defined. If the mail entered does not exist as a mail of a user, there will be no answer

Resources:

github repository

tutorial


Return to home