Cómo crear un sistema de Autenticación del backend personalizado en Django
- 👤 Andrés Cruz
Contar con un sistema de autenticación es de los aspectos más básicos en cualquier aplicación web, aunque hasta este momento hemos podido solventar la necesidad aprovechando el módulo de autenticación que provee Django Admin no es posible emplearlo para usuarios que no sean administradores y es por eso que en este apartado vamos a aprender a crear un módulo de autenticación personalizada.
El enlace a la documentación oficial que emplearemos en este post (recuerda que ya sabemos como Desplegar Django en producción con Railway):
https://docs.djangoproject.com/en/dev/topics/auth/default/
De momento, solo podemos acceder al módulo de Admin para autenticarnos, pero no tenemos otra forma para el usuario final.
En Django existen varias maneras de manejar esto. La que viene por defecto es para el superusuario, y para un usuario regular no funcionaría autenticarse por ahí por razones obvias, principalmente por temas de permisos. Como vimos al final de la sección anterior, cuando intenté acceder al Admin siendo un usuario regular, el sistema me rebotó.
Para solucionar esto, vamos a implementar un módulo de autenticación personalizado. Existen tres rutas principales:
- Rutas integradas de Django: Emplear un paquete de rutas que ya vienen listas para utilizar y simplemente sobreescribirlas con el estilo que deseemos. Esta es la ruta que seguiremos ahora.
- Paquetes externos: Instalar un paquete más robusto (seguramente te lo mostraré al final como demostración), que es el que utilizo en proyectos más grandes, como tiendas en línea.
- Vistas basadas en clases: Tal cual implementamos antes, también existen VBC que podemos emplear para la autenticación.
Exponer rutas de para la autenticación de usuario
Hay ciertos templates que podemos sobrescribir de una manera directa, para ello, debemos de incluir las rutas para manejar el módulo de usuario:
customlogin\customlogin\urls.py
urlpatterns = [
***
path('accounts/', include('django.contrib.auth.urls')),
]Aplicamos las migraciones
$ python manage.py migrateCreamos un usuario superadministrador para poder emplear Django Admin:
$ python manage.py createsuperuserCon esto, exponemos las siguientes rutas:
- 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']
Podemos personalizar el template de las rutas anteriores; tan solo hay que crear dentro de la carpeta templates una carpeta llamada registration seguido de alguno de los siguientes archivos:
- customlogin\auth\templates\registration\login.html - Para el login
- customlogin\auth\templates\registration\password_change_done.html - Para el login
- customlogin\auth\templates\registration\password_change_form.html - Para el password_change_form
- customlogin\auth\templates\registration\password_change_done.html - Para el password_change_done
- customlogin\auth\templates\registration\password_reset_form.html - Para el password_reset
- customlogin\auth\templates\registration\password_reset_confirm.html - Para el password_reset_confirm
- customlogin\auth\templates\registration\password_reset_complete.html - Para el password_reset_complete
Estos templates se pueden personalizar por cada aplicación en Django en caso de que sea necesario.
Construcción del Formulario de Login
Por ejemplo, vamos a definir el template más sencillo para el login:
account/templates/registration/login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
</head>
<body>
{% if next %}
If you want to access {{ next }} authenticate!
{% endif %}
<form action="{% url 'login' %}" method="post">
{% csrf_token %}
{{ form.username.label }}
{{ form.username }}
{{ form.password.label }}
{{ form.password }}
<input type="submit" value="Send">
</form>
<a href="{% url 'password_reset' %}">Forged Password?</a>
</body>
</html>Como puedes apreciar, tenemos total libertad de personalizar el template para el login, más no su vista.
Como puntos importantes tenemos:
El uso de la comprobación de la variable next, que puede estar presente si el usuario intenta ingresar a una ruta protegida por login pero el usuario no está autenticado; por ejemplo:
http://127.0.0.1:8000/accounts/login/?next=/admin/
next tendría el valor de:
/admin/
También personalizamos el template anterior creando una URL para cuando el usuario no recuerda su contraseña.
El resto del código es un formulario tradicional en Django; si intentas ingresar a la siguiente URL:
http://127.0.0.1:8000/accounts/login/
Verás una pantalla como la siguiente:

Figura 17-1: Formulario de Login
En la cual te puedes autenticar una vez ejecutadas las migraciones y creado un usuario.
Instalar Bootstrap 5 y Font Awesome
En esta sección principalmente vamos a querer personalizar algunos templates y conocer sus comportamientos; una de las claves de esta personalización es que tenga un buen diseño, y es por eso que volveremos a emplear Bootstrap.
Comencemos instalando Bootstrap; al ser Django un framework sin ninguna vinculación con Node, debemos de usar las CDN de Bootstrap la cual podemos obtener desde la página oficial:
Presionas sobre "Download" y luego nuevamente sobre "Download" en el siguiente apartado:

Figura 17-1: CDN de Bootstrap
Bootstrap, al ser un framework web del lado del cliente, usa CSS y JavaScript para poder utilizar cualquier componente o funcionalidad de Bootstrap; una vez descargado el comprimido anterior y generada la carpeta (la cual, para simplificar el proceso, renombramos como bootstrap) necesitamos los siguientes archivos:
account/static/
bootstrap/css/bootstrap.min.css
bootstrap/js/bootstrap.min.js
Que vamos a copiar dentro de la carpeta static del proyecto:

Figura 17-2: Bootstrap y Fontawesome instalados en el proyecto
Tambien instalamos las dependencias de popper que puedes obtener desde la misma página de Bootstrap; instalamos Font Awesome para la iconografía:
<link rel="stylesheet" href="{% static 'fontawesome\css\all.min.css' %}">Recuerda descargar los archivos fuentes para web de manera gratuita de:
Con Fontawesome tenemos acceso a múltiples iconos en SVG ya listos para emplear.
También existe un paquete específico para Django en el caso de que lo prefieras configurar de esa manera:
https://fontawesome.com/docs/web/use-with/python-django
Especificación de atributos HTML con django-widget-tweaks
Lo siguiente que vamos a hacer es instalar el paquete Django Widget Tweaks. Como te comentaba anteriormente al hablar de formularios, uno de los retos en Django es personalizar los atributos HTML (como las clases de CSS) cuando no tenemos acceso directo al archivo forms.py.
En este caso, al usar los formularios integrados del núcleo de Django para la autenticación, no podemos editarlos fácilmente. Por defecto, estos campos vienen sin estilos, pero para que funcionen correctamente con Bootstrap, necesitamos inyectarles clases como form-control. Para lograr este mecanismo de personalización, utilizaremos este paquete:
https://pypi.org/project/django-widget-tweaks/
Mediante:
$ pip install django-widget-tweaksRegistramos la aplicación:
customlogin\customlogin\settings.py
INSTALLED_APPS = [
***
'widget_tweaks',
]Funcionalidades principales:
Agregar Clases: Mediante el filtro add_class, podemos añadir las clases de Bootstrap directamente en el HTML del template.
Personalizar Atributos: Si tienes un textarea o cualquier otro input, puedes usar attr para definir atributos adicionales (como un placeholder o un rows) indicando el nombre del atributo y su valor.
{% load widget_tweaks %}
{{ form.username|add_class:"form-control" }}
{{ form.password|attr:"placeholder:Ingresa tu contraseña"|add_class:"form-control" }}Con esto ya tenemos todo preparado para empezar a trabajar a fondo con nuestros formularios y crear un módulo de autenticación propio y profesional a partir de la siguiente sección.
Es importante notar que, al momento de emplear el de add_label_class se hace es directamente sobre el campo de formulario y no sobre el label, ya el plugin al emplear el filtro anterior agrega las clases en el label y retorna el label en vez del campo de formulario, es decir, no debemos de usar la siguiente sintaxis para agregar clases a los labels:
{{ form.username.label|add_label_class:"form-label" }}Si no la anterior, ya que esta sintaxis daría un error.
Plantilla maestra
Creamos la plantilla maestra en donde importamos Bootstrap y Fontawesome y empleamos la clase container de Bootstrap para evitar que el contenido aparezca todo estirado con un tamaño fijo para que no ocupe todo el ancho:
account\templates\base_user_account.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Account</title>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap\css\bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'fontawesome\css\all.min.css' %}">
</head>
<body>
<div class="container" style="max-width: 500px;">
{% block content %}
{% endblock %}
</div>
<script src="{% static 'popper.min.js' %}"></script>
<script src="{% static 'bootstrap\js\bootstrap.min.js' %}"></script>
</body>
</html>Plantillas de autenticación
Configurado nuestro proyecto con las tecnologías de Bootstrap y Font Awesome, definimos los templates para el resto de las plantillas que tenemos pendiente incluyendo la de login pero maquetado con Bootstrap y Font Awesome.
account\templates\registration\login.html
{% extends "base_user_account.html" %}
{% load widget_tweaks %}
{% block content %}
<div class="card mt-5">
<div class="card-header">
<h4>Login</h4>
</div>
<div class="card-body">
{% if next %}
<div class="alert alert-warning">
If you want to access {{ next }} authenticate!
</div>
{% endif %}
<form action="{% url 'login' %}" method="post">
{% csrf_token %}
<div class="mb-3">
{{ form.username|add_label_class:"form-label" }}
{{ form.username|add_class:"form-control" }}
</div>
<div class="mb-3">
{{ form.password|add_label_class:"form-label" }}
{{ form.password|add_class:"form-control" }}
</div>
<div class="d-grid">
<input type="submit" value="Send" class="btn btn-primary">
</div>
</form>
<a class="float-end mt-3" href="{% url 'password_reset' %}">Forged Password?</a>
</div>
</div>
{% endblock %}Y una plantilla de ayuda para mostrar mensajes de alertas en la cual podemos personalizar el mensaje y el color mediante el tag:
customlogin\account\templates\partials\alert.html
{% if message %}
<div class="alert {% if tag %}
alert-{{tag}}
{% else %}
alert-info
{% endif %}">
{{ message }}
</div>
{% endif %}Puntos importantes
- Usamos el componente de carta como elemento contenedor del formulario.
- Aquí es donde aplicamos el paquete que instalamos. Recuerda que la etiqueta {% load widget_tweaks %} debe ir en cada plantilla donde la uses, ya que no se hereda de forma global. Usamos las clases de form-label y form-control para el label y campo de formulario respectivamente para emplear el CSS de Bootstrap.
- Usamos el componente de alerta para mostrar mensajes de advertencia como es en este caso, la ruta protegida a donde el usuario intentó ingresar.
- Usamos la clase float-end para flotar el enlace a la derecha.
- Usamos las clases de mt-3 y mb-3 para agregar un margen de 3 niveles al top y bottom.
- Usamos la clase d-grid en el botón para que ocupe todo el ancho.
Recuerda que los cambios anteriores son solamente en CSS y HTML y puedes personalizarlos a gusto.
Cómo puedes apreciar, existe una estructura común como el diseño del formulario que incluye las clases, manejo de los errores y estructura en general que podemos replicar fácilmente al resto de los formularios.
El resto de las pantallas maquetadas las puedes obtener desde el repositorio al curso y libro completo al final de este post.
Extra: Crear un backend de autenticación para el login personalizado en Django
En este punto seguramente saber que Django cuenta con un módulo de login que principalmente está pensado para trabajar con el admin; todo esto viene siendo de gratis al crear simplemente un proyecto en Django, ya que aplicaciones como la de admin y login (auth):
INSTALLED_APPS = [
'account',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]Vienen de gratis al momento de crear un proyecto en Django, ahora, seguramente en algún punto te interese crear un proceso personalizado para el login, ya sea en reemplazo del original, o más interesante, para emplearlo en paralelo; para esto en nuestro archivo de configuraciones; settings.py, tenemos el siguiente apartado (que tenemos que agregar si queremos crear un login personalizado en Django):
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'account.authentication.AuthByEmailBackend',
]En el cual podemos configurar nuestros backends de autenticación o para realizar el login, como lo quieras ver; como puedes ver, es una lista, un iterable por lo tanto podemos definir más de uno y esto es excelente ya que Django va a ir uno a uno preguntando por la combinación de usuario y contraseña que establezcamos en la aplicación uno a uno, y si el primero en la lista de falso va por el segundo y así hasta cubrirlos todos; como puedes ver, es un comportamiento que nos permite extender el funcionamiento básico de una aplicación en Django para el manejo del login.
El primer backend que define, sería el que emplea Django por defecto; el siguiente sería un archivo que define una clase que vamos a crear en esta entrada.
Creando nuestra clase de autenticación (Login personalizado)
En esta entrada vamos a ver como crear un backend personalizado con Django; para esto, lo primero que tenemos que hacer es crear una clase, es básicamente todo lo que necesitamos, una clase que luego podemos registrar en el listado que vimos anteriormente sobre nuestro settings.py; esta clase la crear claro está en un archivo con cualquier nombre y que puede estar en cualquier aplicación de tu proyecto; por ejemplo, una aplicación llamada:
python manage.py startapp accountEn la cual vamos a crear un archivo llamado authentication.py con el siguiente contenido:
from django.contrib.auth.models import User
"""
Login via email
"""
class AuthByEmailBackend:
def authenticate(self, request, username=None, password=None):
try:
user = User.objects.get(email=username)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return NoneComo puedes ver, es una clase cualquiera, no tenemos que heredar de nada, pero si tiene que tener un método con el nombre de authenticate que reciba el request, por si quieres pasar algún dato adicional, y de manera obligatoria el usuario y la contraseña.
Por lo demás, todo queda en hacer las comprobaciones que quieras hacer para el login, en este ejemplo, para expandir la aplicación, buscamos también por el email y luego chequeamos pos el hash del password que emplea internamente Django para la creación de los usuarios; ahora con esto, vamos a nuestro settings.py y agregamos la referencia a la nueva clase:
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'user.authentication.AuthByEmailBackend'
]Recuerda que en tu backend personalizado puedes definir cualquier tipos de reglas para hacer tu login personalizado.
El siguiente paso, aprende a usar mediante Node Tailwind CSS con Django.
Acepto recibir anuncios de interes sobre este Blog.
Aprende a personalizar el sistema de autenticación de Django desde cero. En esta guía cubrimos cómo crear un backend para login por email y cómo maquetar todas las vistas (reset, login, logout) usando Bootstrap 5 para un acabado profesional.