Uno de los principales apartados cuando desarrollamos un sistema es la Seguridad, en específico, la denominada Autenticación, la cual no es mas que un conjunto de mecanismos para asociar la solicitud entrante con un conjunto de credenciales de identificación que puede contener el usuario del cual proviene la petición o el token perteneciente a un usuario, dependiendo los mecanismos que se hayan definido, se valida si la solicitud puede ser permitida o no.

Sabemos que la autenticación siempre se ejecuta al comienzo de la vista antes de que se produzcan las comprobaciones de permisos y limitaciones y antes de cualquier procesamiento de datos en alguna vista, por ello Django Rest Framework, luego de validar la petición, guardará la instancia de la clase User en request.user. Además de ello, existe otra propiedad llamada request.auth, la cual se utiliza para cualquier información adicional de la autenticación, por ejemplo, representar el Token para la Autenticación.

Configuración de Autenticación

Django Rest Framework trae una configuración de Autenticación por defecto, la cuál es la siguiente:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ]
}

Esta configuración es editable y representa una configuración de Autenticación Global, todas las vistas no se ejecutan si es que la solicitud no es permitida por las valicaciones que se han definido en estas 2 clases: BasicAuthentication y SessionAuthentication.

Esta configuración no sólo se puede indicar de forma global, podemos utilizarlas para que sólo se apliquen a una vista en específico, para esto podemos utilizar el atributo authentication_classes que contiene toda Vista Basada en Clase de Django Rest Framework.

from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

class ExampleView(APIView):
    authentication_classes = [SessionAuthentication, BasicAuthentication]
    permission_classes = [IsAuthenticated]

    def get(self, request, format=None):
        content = {
            'user': str(request.user),  # `django.contrib.auth.User` instance.
            'auth': str(request.auth),  # None
        }
        return Response(content)

Además, no sólo se puede utilizar para Vistas Basadas en Clases, sino también para funciones utilizadas como Vistas.

@api_view(['GET'])
@authentication_classes([SessionAuthentication, BasicAuthentication])
@permission_classes([IsAuthenticated])
def example_view(request, format=None):
    content = {
        'user': str(request.user),  # `django.contrib.auth.User` instance.
        'auth': str(request.auth),  # None
    }
    return Response(content)

BasicAuthentication

Ahora que conocemos cómo podemos utilizar las clases por defecto de Autenticación que Django Rest Framework nos ofrece, debemos conocer para qué se utilizan.

BasicAuthenticacion no es más que la autenticación básica que se implementa desde Django, es decir, basada en un USUARIO Y CONTRASEÑA, es un esquema de autenticación básico utilizando HTTP.

Una autenticación correcta con este método, colocará las siguientes credenciales en el request:

  • request.user será una instancia del modelo User que se halla definido.
  • request.auth será None

Toda respuesta que no se ha autenticado se les negará el permiso y darán como resultado una respuesta HTTP 401 Unauthorized.

SessionAuthentication

Este esquema utiliza el framework incorporado de Sesiones que trae Django y lo utiliza para la Autenticación. Esta es apropiada para clientes AJAX que se ejecutan en el mismo contexto de sesión que el sitio web.

Una autenticación correcta con este método, colocará las siguientes credenciales en el request:

  • request.user será una instancia del modelo User que se halla definido.
  • request.auth será None

Toda respuesta que no se ha autenticado se les negará el permiso y darán como resultado una respuesta HTTP 403 Forbidden .

Si se utiliza una API con AJAx y SesionAuthentication, se deberá incluir un CSRF Token válido para cualquier llamado HTTP no seguro de tipo PUT, PATCH, POST, DELETE .

TokenAuthentication

Este tipo de autenticación utiliza un esquema HTTP simple basado en Token, un Token es un secuencia de caracteres especiales encriptados que representan información del usuario al cual se asocian. Este tipo de autenticación es ideal para configuraciones a proyectos cliente-servidor, donde hay clientes de varios tipos, puede ser escritorio o móvil.

Esta clase, incorporada en Django Rest Framework puede utilizarse como Autenticación Global, para ello sólo debemos aplicarla de la misma forma que SessionAuthentication y BasicAuthentication, adicionalmente debemos agregar rest_framework.authtoken a nuestro archivo de configuración.

Cuando se realiza este tipo de Autenticación, utilizando esta clase, la clave del token debe incluirse en el header de la petición HTTP, debe tener como prefijo la palabra Token seguido de un espacio y luego el Token en cuestión.

Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b

Si la autenticación es realizada correctamente, colocará las siguientes credenciales en el request:

  • request.user será una instancia del modelo User que se halla definido.
  • request.auth será será una instancia de rest_framework.authtoken.models.Token.

Toda respuesta que no se ha autenticado se les negará el permiso y darán como resultado una respuesta HTTP 401 Unauthorized .

Un ejemplo de obtención de un Token para un usuario en el proceso de Login sería el siguiente:

from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response

class CustomAuthToken(ObtainAuthToken):

    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class(data=request.data,
                                           context={'request': request})
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data['user']
        token, created = Token.objects.get_or_create(user=user)
        return Response({
            'token': token.key,
            'user_id': user.pk,
            'email': user.email
        })

Custom Authentication

Django Rest Framework nos permite crear nuestra propia clase Autenticación cumpliendo ciertas reglas para que el funcionamiento general no se vea afectado.

Nuestra clase debe heredar de BaseAuthentication y debemos sobreescribir el método .authenticate(self, request), este método debe devolver 2 valores en forma de tupla: (usuario, autenticacion) si es que todo ha salido bien y None si ha salido mal, sin embargo, en vez de devolver None podemos generar una excepción con AuthenticationFailed para un mejor control, por lo que de forma general será:

  • Si no se intenta autenticar en la petición, se devuelve None y se procederá a comprobar cualquier otro esquema de Autenticación que el sistema tenga implementado.
  • Si se intenta autenticar en la petición y esta falla, se genera una excepción con AuthenticationFailed y devolverá un mensaje de error con el mensaje que se le haya programado.

Además del método .authenticate(self, request), podemos sobreescribir el método .authenticate_header(self, request) el cuál va a devolver una cadena que representa el valor del encabezado WWW-Authenticate

Un ejemplo de esto sería:

from django.contrib.auth.models import User
from rest_framework import authentication
from rest_framework import exceptions

class ExampleAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        username = request.META.get('HTTP_X_USERNAME')
        if not username:
            return None

        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            raise exceptions.AuthenticationFailed('No such user')

        return (user, None)

Si quieres ver de forma mas detallada todo esto, te dejo un vpideo de nuestro canal, donde explicamos todo este tema.

Otros Posts


Convertir Instancia de un Modelo de Django a un Diccionario
12 de Febrero de 2022 • Oliver Sandoval
Diferentes formas de convertir una instancia de un Modelo de Django a un Diccionario
Leer mas »
Configurar Multiples Bases de Datos con Django
21 de Octubre de 2021 • Oliver Sandoval
Multiples Bases de Datos con Django
Leer mas »
Autenticación en Django Rest Framework
4 de Septiembre de 2021 • Oliver Sandoval
Autenticacion en Django Rest Framework
Leer mas »