{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[],"collapsed_sections":["7Preg6GKovMU","XcvoR0drpQHL","K6lKY3QLwYBJ"],"authorship_tag":"ABX9TyMZZVRjwe4w0apfWFvWCODx"},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"markdown","source":["# Flujo de Autenticación con JWT"],"metadata":{"id":"y6wqxnX2UvTT"}},{"cell_type":"markdown","source":["## Construir un API REST con Django REST Framework"],"metadata":{"id":"7Preg6GKovMU"}},{"cell_type":"markdown","source":["Crea un ambiente virtual:\n","```\n","python3 -m venv env\n","```\n","Activa el ambiente virtual:\n","```\n","# Activación en Unix\n","source env/bin/activate\n","\n","# Activación en Windows\n","env\\Scripts\\activate\n","```\n","Instala Django, DRF y PyJWT:\n","```\n","pip install django\n","pip install djangorestframework\n","pip install pyjwt\n","```\n","Crea un nuevo proyecto en Django:\n","```\n","django-admin startproject jwt_auth\n","```\n","Crea una nueva aplicación en Django:\n","```\n","python manage.py startapp api\n","```\n","Agrega la aplicación de `rest_framework` y la que acabamos de crear en el archivo de `settings.py`:"],"metadata":{"id":"wgcp2D0Tk89M"}},{"cell_type":"code","execution_count":null,"metadata":{"id":"IIniE4AHUneY"},"outputs":[],"source":["# Modificamos jwt_auth/settings.py\n","\n","# Especificar qué aplicaciones están habilitadas y disponibles para ser usadas por el proyecto.\n","INSTALLED_APPS = [\n","    'django.contrib.admin',\n","    'django.contrib.auth',\n","    'django.contrib.contenttypes',\n","    'django.contrib.sessions',\n","    'django.contrib.messages',\n","    'django.contrib.staticfiles',\n","    'rest_framework',\n","    'api'\n","]"]},{"cell_type":"markdown","source":["## Registro de usuarios"],"metadata":{"id":"XcvoR0drpQHL"}},{"cell_type":"markdown","source":["### Define un serializador para el modelo User\n","\n"],"metadata":{"id":"g6F4ggH9n9D1"}},{"cell_type":"code","source":["# Creamos api/serializers.py\n","\n","# Importa el modelo User de Django, que es el modelo de usuario predeterminado utilizado para la autenticación y la gestión de usuarios en proyectos de Django.\n","from django.contrib.auth.models import User\n","# Importa el módulo serializers de Django REST Framework.\n","from rest_framework import serializers\n","\n","\n","# Define una nueva clase UserSerializer.\n","class UserSerializer(serializers.ModelSerializer):\n","    # Esta subclase Meta se utiliza para especificar opciones de metadatos para el serializador UserSerializer.\n","    class Meta:\n","        # Indica que el serializador está asociado con el modelo User.\n","        model = User\n","        # Especifica una lista de nombres de campos del modelo User que se incluirán en el serializado/deserializado.\n","        fields = [\"username\", \"email\", \"password\"]\n","        # Esta línea establece argumentos adicionales para los campos especificados en fields.\n","        extra_kwargs = {\"password\": {\"write_only\": True}}"],"metadata":{"id":"N7tEdBw6ncmr"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["### Define las vistas diseñadas para manejar operaciones CRUD (Crear, Leer, Actualizar, Borrar) sobre usuarios."],"metadata":{"id":"OIkeGlO2qlFD"}},{"cell_type":"code","source":["# Modificamos api/views.py\n","\n","# Importa el modelo User, el modelo de usuario predeterminado de Django.\n","from django.contrib.auth.models import User\n","# Importa el módulo status que contiene códigos de estado HTTP para las respuestas.\n","from rest_framework import status\n","# Importa la clase Response que se utiliza para retornar respuestas desde las vistas de DRF.\n","from rest_framework.response import Response\n","# Importa APIView, la clase base para todas las vistas en DRF.\n","from rest_framework.views import APIView\n","# Importa el serializador UserSerializer.\n","from .serializers import UserSerializer\n","\n","\n","# Define una vista UserViews\n","class UserViews(APIView):\n","    # Establece UserSerializer como el serializador por defecto para esta vista.\n","    serializer_class = UserSerializer\n","\n","    # Define cómo manejar GET, para listar todos los usuarios.\n","    def get(self, request):\n","        # Obtiene todos los usuarios de la base de datos.\n","        users = User.objects.all()\n","        # Serializa la lista de usuarios utilizando UserSerializer\n","        serializer = UserSerializer(users, many=True)\n","        # Retorna una respuesta indicando el éxito de la operación.\n","        return Response(\n","            {\"status\": \"OK\", \"data\": serializer.data},\n","            status=status.HTTP_200_OK,\n","        )\n","\n","    # Define cómo manejar POST, para crear un nuevo usuario.\n","    def post(self, request):\n","        # Crea una instancia de UserSerializer con los datos enviados en la solicitud.\n","        serializer = UserSerializer(data=request.data)\n","        # Verifica si los datos son válidos.\n","        if serializer.is_valid():\n","            # Guarda el usuario en la base de datos.\n","            serializer.save()\n","            # Retorna una respuesta indicando el éxito de la operación.\n","            return Response(\n","                {\"status\": \"CREATED\", \"data\": serializer.data},\n","                status=status.HTTP_201_CREATED,\n","            )\n","        # Retorna una respuesta indicando el fallo de la operación.\n","        return Response(\n","            {\"status\": \"BAD_REQUEST\", \"data\": serializer.errors},\n","            status=status.HTTP_400_BAD_REQUEST,\n","        )\n","\n","\n","# Define una vista UserDetailViews\n","class UserDetailViews(APIView):\n","    # Establece UserSerializer como el serializador por defecto para esta vista.\n","    serializer_class = UserSerializer\n","\n","    # Define cómo manejar GET, para obtener detalles de un usuario específico.\n","    def get(self, request, username):\n","        # Recupera una instancia del modelo User\n","        user = User.objects.get_by_natural_key(username)\n","        # Crea una instancia de UserSerializer, pasando el usuario obtenido como el objeto a serializar.\n","        serializer = UserSerializer(user)\n","        # # Retorna una respuesta indicando el éxito de la operación.\n","        return Response(\n","            {\"status\": \"OK\", \"data\": serializer.data},\n","            status=status.HTTP_200_OK,\n","        )\n","\n","    # Define cómo manejar PATCH, para actualizar parcialmente recursos.\n","    def patch(self, request, username):\n","        # Recupera una instancia del modelo User\n","        user = User.objects.get_by_natural_key(username)\n","        # Crea una instancia de UserSerializer, esta vez pasando tanto el objeto user como los datos provenientes de la solicitud.\n","        serializer = UserSerializer(user, data=request.data, partial=True)\n","        # Verifica si los datos son válidos.\n","        if serializer.is_valid():\n","            # Guarda el usuario en la base de datos.\n","            serializer.save()\n","            # Retorna una respuesta indicando el éxito de la operación.\n","            return Response(\n","                {\"status\": \"OK\", \"data\": serializer.data},\n","                status=status.HTTP_200_OK,\n","            )\n","        # Retorna una respuesta indicando el fallo de la operación.\n","        return Response(\n","            {\"status\": \"BAD_REQUEST\", \"data\": serializer.errors},\n","            status=status.HTTP_400_BAD_REQUEST,\n","        )\n","\n","    # Define cómo manejar DELETE, para eliminar un usuario específico.\n","    def delete(self, request, username):\n","        # Recupera una instancia del modelo User\n","        user = User.objects.get_by_natural_key(username)\n","        # Elimina el usuario encontrado de la base de datos.\n","        user.delete()\n","        # Retorna una respuesta indicando el éxito de la operación.\n","        return Response(\n","            {\"status\": \"OK\", \"data\": \"User deleted.\"},\n","            status=status.HTTP_200_OK,\n","        )"],"metadata":{"id":"1DspZrdvq_dX"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["### Asigna URLs específicas a vistas definidas en la aplicación."],"metadata":{"id":"NuopRonsunpU"}},{"cell_type":"code","source":["# Creamos api/urls.py\n","\n","# Importa la función path del módulo django.urls, que se utiliza para definir patrones de URL individuales.\n","from django.urls import path\n","# Importa las vistas UserDetailViews y UserViews.\n","from .views import UserDetailViews, UserViews\n","\n","# Cada elemento de esta lista es una llamada a la función path, que define una asociación entre un patrón de URL y una vista.\n","urlpatterns = [\n","    path(\"users/\", UserViews.as_view()),\n","    path(\"users/<str:username>\", UserDetailViews.as_view()),\n","]"],"metadata":{"id":"_apQrUt1rLiJ"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["### Configura las rutas URL en el proyecto Django"],"metadata":{"id":"Kc2U2ElQvlv1"}},{"cell_type":"code","source":["# Modificamos jwt_auth/urls.py\n","\n","# Importa el módulo de administración de Django.\n","from django.contrib import admin\n","# Importa las funciones path e include del módulo django.urls.\n","# path se utiliza para definir patrones de URL individuales.\n","# include permite referenciar definiciones de URL de otras aplicaciones Django.\n","from django.urls import path, include\n","\n","# Cada elemento de esta lista es una llamada a la función path, que define una asociación entre un patrón de URL y una vista.\n","urlpatterns = [\n","    path(\"admin/\", admin.site.urls),\n","    path(\"api/\", include('api.urls')),\n","]\n"],"metadata":{"id":"7YUoYP2LvoDj"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["## Inicio de sesión y Generación de JWT"],"metadata":{"id":"xTJ9DqQYwOf6"}},{"cell_type":"markdown","source":["Crea una nueva aplicación en Django:\n","```\n","python manage.py startapp accounts\n","```\n","Agrega la aplicación de `rest_framework` y la que acabamos de crear en el archivo de `settings.py`:"],"metadata":{"id":"5Vq7ObxJq1e1"}},{"cell_type":"code","source":["# Modificamos jwt_auth/settings.py\n","\n","# Especificar qué aplicaciones están habilitadas y disponibles para ser usadas por el proyecto.\n","INSTALLED_APPS = [\n","    'django.contrib.admin',\n","    'django.contrib.auth',\n","    'django.contrib.contenttypes',\n","    'django.contrib.sessions',\n","    'django.contrib.messages',\n","    'django.contrib.staticfiles',\n","    'rest_framework',\n","    'api',\n","    'accounts',\n","]"],"metadata":{"id":"7m2KDFixq9e1"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["### Define un serializador para el Log in"],"metadata":{"id":"FeIfzNDWwhBr"}},{"cell_type":"code","source":["# Creamos accounts/serializers.py\n","\n","# Importa el módulo serializers de Django REST Framework.\n","from rest_framework import serializers\n","\n","\n","# Define una nueva clase LoginSerializer.\n","class LoginSerializer(serializers.Serializer):\n","    # Se define un campo username\n","    username = serializers.CharField()\n","    # Se define un campo password\n","    password = serializers.CharField()"],"metadata":{"id":"KyT258B9wngq"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["### Define una función que genera un token JWT para un usuario específico."],"metadata":{"id":"r014Y2O4yHil"}},{"cell_type":"code","source":["# Creamos accounts/utils.py\n","\n","# Importa las clases datetime y timedelta del módulo datetime. datetime se utiliza para obtener el tiempo actual en formato UTC, y timedelta para definir un periodo de tiempo.\n","from datetime import datetime, timedelta\n","\n","# Importa el módulo jwt.\n","import jwt\n","# Importa el módulo settings de Django.\n","from django.conf import settings\n","# Importa el modelo User\n","from django.contrib.auth.models import User\n","from django.contrib.auth import authenticate\n","\n","\n","# Generar y retornar un token JWT para el usuario proporcionado.\n","def generate_jwt_token(user: User):\n","    # Contiene las reclamaciones (claims) que se incluirán en el token JWT.\n","    payload = {\n","        \"user_pk\": user.pk,\n","        \"username\": user.username,\n","        \"exp\": datetime.utcnow() + timedelta(hours=1),\n","    }\n","    # Retorna el resultado de codificar el payload en un token JWT\n","    return jwt.encode(payload, settings.SECRET_KEY, algorithm=\"HS256\")\n"],"metadata":{"id":"7Qv8Tqp6ySU_"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["### Define una vista para manejar solicitudes de inicio de sesión y generar tokens JWT para usuarios autenticados."],"metadata":{"id":"LgwYsFyn0dsQ"}},{"cell_type":"code","source":["# Modificamos accounts/views.py\n","\n","# Importa el módulo status que contiene códigos de estado HTTP para las respuestas.\n","from rest_framework import status\n","# Importa la clase Response que se utiliza para retornar respuestas desde las vistas de DRF.\n","from rest_framework.response import Response\n","# Importa APIView, la clase base para todas las vistas en DRF.\n","from rest_framework.views import APIView\n","\n","# Importa LoginSerializer\n","from .serializers import LoginSerializer\n","# Importa la función generate_jwt_token\n","from .utils import generate_jwt_token\n","\n","\n","# Define una vista LoginViews\n","class LoginView(APIView):\n","    # Establece UserSerializer como el serializador por defecto para esta vista.\n","    serializer_class = LoginSerializer\n","\n","    # Define cómo manejar POST, para enviar datos de inicio de sesión.\n","    def post(self, request):\n","        # Crea una instancia de LoginSerializer con los datos enviados en la solicitud.\n","        serializer = LoginSerializer(data=request.data)\n","        # Verifica si los datos son válidos.\n","        if serializer.is_valid():\n","            # Obtiene el nombre de usuario de los datos validados.\n","            username = serializer.validated_data[\"username\"]\n","            # Obtiene la contraseña de los datos validados.\n","            password = serializer.validated_data[\"password\"]\n","            # Intenta autenticar al usuario utilizando la función authenticate.\n","            user = authenticate(request, username=username, password=password)\n","            # Verifica si la autenticación fue exitosa.\n","            if user is not None:\n","                # Genera un token JWT para el usuario\n","                token = generate_jwt_token(user)\n","                # Retorna una respuesta indicando el éxito de la operación.\n","                return Response(\n","                    {\"status\": \"OK\", \"access_token\": token},\n","                    status=status.HTTP_200_OK,\n","                )\n","            # Retorna una respuesta indicando el fallo de la operación.\n","            return Response(\n","                {\n","                    \"status\": \"UNAUTHORIZED\",\n","                    \"data\": \"The username or password you are trying to log in\"\n","                    \" with are incorrect.\",\n","                },\n","                status=status.HTTP_401_UNAUTHORIZED,\n","            )\n","        # Retorna una respuesta indicando el fallo de la operación.\n","        return Response(\n","            {\"status\": \"BAD_REQUEST\", \"data\": serializer.errors},\n","            status=status.HTTP_400_BAD_REQUEST,\n","        )"],"metadata":{"id":"GwWGRhO80mjc"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["### Asigna URLs específicas a vistas nuevas definidas en la aplicación."],"metadata":{"id":"8Pg3PUFT2MrS"}},{"cell_type":"code","source":["# Creamos accounts/urls.py\n","\n","# Importa las vistas LoginView.\n","from .views import LoginView\n","\n","# Cada elemento de esta lista es una llamada a la función path, que define una asociación entre un patrón de URL y una vista.\n","urlpatterns = [\n","    path(\"login/\", LoginView.as_view()),\n","]"],"metadata":{"id":"C4VX4kWK2Tp4"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["### Configura las rutas URL en el proyecto Django"],"metadata":{"id":"NiWdfNNRr1Yj"}},{"cell_type":"code","source":["# Modificamos jwt_auth/urls.py\n","\n","# Importa el módulo de administración de Django.\n","from django.contrib import admin\n","# Importa las funciones path e include del módulo django.urls.\n","# path se utiliza para definir patrones de URL individuales.\n","# include permite referenciar definiciones de URL de otras aplicaciones Django.\n","from django.urls import path, include\n","\n","# Cada elemento de esta lista es una llamada a la función path, que define una asociación entre un patrón de URL y una vista.\n","urlpatterns = [\n","    path(\"admin/\", admin.site.urls),\n","    path(\"api/\", include('api.urls')),\n","    path(\"\", include(\"accounts.urls\")),\n","]\n"],"metadata":{"id":"7laqDyIXr8qh"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["## Validación y Verificación de JWT"],"metadata":{"id":"KnyINQTLG4Qj"}},{"cell_type":"markdown","source":["### Define un middleware personalizado en Django para manejar la autenticación JWT en las solicitudes HTTP entrantes a la aplicación."],"metadata":{"id":"n_lEJZolHG5S"}},{"cell_type":"code","source":["# Creamos accounts/middlewares.py\n","\n","# Define un middleware personalizado en Django para manejar la autenticación JWT (JSON Web Token) en las solicitudes HTTP entrantes a la aplicación.\n","import jwt\n","# Importa la configuración del proyecto Django.\n","from django.conf import settings\n","# Importa JsonResponse, que es una forma conveniente de devolver respuestas JSON desde una vista Django.\n","from django.http import JsonResponse\n","# Importa el módulo status de Django REST Framework.\n","from rest_framework import status\n","# Importa la clase Request de Django REST Framework\n","from rest_framework.request import Request\n","\n","\n","# Define la función jwt_auth_middleware, que acepta un argumento get_response.\n","# get_response es una función de callback que Django llama con el objeto request para obtener la respuesta de la vista o del siguiente middleware.\n","def jwt_auth_middleware(get_response):\n","\n","    def middleware(request: Request):\n","        # Comprueba si la cabecera HTTP Authorization está presente en la solicitud.\n","        if \"HTTP_AUTHORIZATION\" in request.META:\n","            # Extrae el valor de la cabecera Authorization.\n","            auth_header = request.META[\"HTTP_AUTHORIZATION\"]\n","            try:\n","                # Divide el valor de la cabecera para separar el tipo de autenticación (generalmente \"Bearer\") del token propiamente dicho.\n","                _, token = auth_header.split()\n","                # Intenta decodificar el token JWT usando la clave secreta del proyecto y el algoritmo 'HS256'.\n","                request.decoded_jwt = jwt.decode(\n","                    token, settings.SECRET_KEY, algorithms=[\"HS256\"]\n","                )\n","                # Si el token es válido, permite que la solicitud continúe hacia la vista o el siguiente middleware.\n","                return get_response(request)\n","            # Captura y maneja el error si el token JWT ha expirado.\n","            except jwt.ExpiredSignatureError:\n","                return JsonResponse(\n","                    {\n","                        \"status\": \"OK\",\n","                        \"data\": \"The token has expired. Please log in again to\"\n","                        \" obtain a new token.\",\n","                    },\n","                    status=status.HTTP_401_UNAUTHORIZED,\n","                )\n","            # Captura y maneja el error si el token JWT es inválido.\n","            except jwt.InvalidTokenError:\n","                return JsonResponse(\n","                    {\n","                        \"status\": \"OK\",\n","                        \"data\": \"The provided token is invalid. Please check \"\n","                        \"the token and try again.\",\n","                    },\n","                    status=status.HTTP_401_UNAUTHORIZED,\n","                )\n","        # Si no se proporciona un token, retorna una respuesta JSON con un mensaje indicando que se requiere un token de autenticación.\n","        else:\n","            return JsonResponse(\n","                {\n","                    \"status\": \"OK\",\n","                    \"data\": \"An authentication token is required to access \"\n","                    \"this resource.\",\n","                },\n","                status=status.HTTP_401_UNAUTHORIZED,\n","            )\n","    # Retorna la función middleware configurada.\n","    return middleware"],"metadata":{"id":"6FMLumPNHHaQ"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["### Agrega el middleware que acabamos de crear en el archivo de `settings.py`:"],"metadata":{"id":"Vhn2nmV6HP4a"}},{"cell_type":"code","source":["# Modificamos jwt_auth/settings.py\n","\n","# Especificar qué middlewares están habilitados y disponibles para ser usadas por el proyecto.\n","\n","MIDDLEWARE = [\n","    \"django.middleware.security.SecurityMiddleware\",\n","    \"django.contrib.sessions.middleware.SessionMiddleware\",\n","    \"django.middleware.common.CommonMiddleware\",\n","    \"django.middleware.csrf.CsrfViewMiddleware\",\n","    \"django.contrib.auth.middleware.AuthenticationMiddleware\",\n","    \"django.contrib.messages.middleware.MessageMiddleware\",\n","    \"django.middleware.clickjacking.XFrameOptionsMiddleware\",\n","    'accounts.middleware.jwt_auth_middleware',\n","]"],"metadata":{"id":"3e80_DvsHQb1"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["### Modificamos middleware para agregar las rutas de URL para las cuales no debe aplicar la lógica de autenticación JWT."],"metadata":{"id":"MffTxyiFHdd4"}},{"cell_type":"code","source":["# Modificamos accounts/middlewares.py\n","\n","# Define la función jwt_auth_middleware, que acepta un argumento get_response.\n","# get_response es una función de callback que Django llama con el objeto request para obtener la respuesta de la vista o del siguiente middleware.\n","def jwt_auth_middleware(get_response):\n","    # Define una lista de endpoints llamada excluded_endpoints que contiene las rutas de URL para las cuales este middleware no debe aplicar la lógica de autenticación JWT.\n","    excluded_endpoints = [\"/login/\"]\n","\n","    def middleware(request: Request):\n","        # Este bloque condicional verifica si la ruta de la solicitud actual está en la lista de excluded_endpoints.\n","        # Si es así, el middleware permite que la solicitud continúe sin procesamiento adicional.\n","        if request.path in excluded_endpoints:\n","            return get_response(request)\n","\n","        # Comprueba si la cabecera HTTP Authorization está presente en la solicitud.\n","        if \"HTTP_AUTHORIZATION\" in request.META:\n","            # Extrae el valor de la cabecera Authorization.\n","            auth_header = request.META[\"HTTP_AUTHORIZATION\"]"],"metadata":{"id":"NTo7h0jHHeHf"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["## Manejo de tokens de actualización y refresco"],"metadata":{"id":"K6lKY3QLwYBJ"}},{"cell_type":"markdown","source":["### Modificamos la función que genera un token JWT para crear y retornar dos tokens JWT: un \"access token\" y un \"refresh token\" para un usuario específico"],"metadata":{"id":"4ICRf6cXx14O"}},{"cell_type":"code","source":["# Modificamos accounts/utils.py\n","\n","# Importa las clases datetime y timedelta del módulo datetime. datetime se utiliza para obtener el tiempo actual en formato UTC, y timedelta para definir un periodo de tiempo.\n","from datetime import datetime, timedelta\n","\n","# Importa el módulo jwt.\n","import jwt\n","# Importa el módulo settings de Django.\n","from django.conf import settings\n","# Importa el modelo User\n","from django.contrib.auth.models import User\n","\n","\n","# Generar y retornar dos token JWT para el usuario proporcionado.\n","def generate_jwt_tokens(user: User):\n","    # Contiene las claims que se incluirán en el JWT para el \"access token\".\n","    access_payload = {\n","        \"user_pk\": user.pk,\n","        \"username\": user.username,\n","        \"exp\": datetime.utcnow() + timedelta(hours=1),\n","    }\n","    # Utiliza la función encode del módulo jwt para codificar el access_payload en un JWT.\n","    access_token = jwt.encode(\n","        access_payload, settings.SECRET_KEY, algorithm=\"HS256\"\n","    )\n","\n","    # Contiene las claims que se incluirán en el JWT para el \"refresh token\".\n","    refresh_payload = {\n","        \"user_pk\": user.pk,\n","        \"username\": user.username,\n","        \"exp\": datetime.utcnow() + timedelta(days=1),\n","    }\n","    # Utiliza la función encode del módulo jwt para codificar el refresh_payload en un JWT.\n","    refresh_token = jwt.encode(\n","        refresh_payload, f\"refresh.{settings.SECRET_KEY}\", algorithm=\"HS256\"\n","    )\n","    # La función retorna una tupla que contiene el \"access token\" y el \"refresh token\".\n","    return access_token, refresh_token"],"metadata":{"id":"b2g6c5kxxfXx"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["### Define un serializador para el refresh token"],"metadata":{"id":"TaP1vwaszlZn"}},{"cell_type":"code","source":["# Modificamos accounts/serializers.py\n","\n","\n","# Define una nueva clase RefreshTokenSerializer.\n","class RefreshTokenSerializer(serializers.Serializer):\n","    # Se define un campo refresh_token\n","    refresh_token = serializers.CharField()"],"metadata":{"id":"vf-Ulv4JzyG6"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["### Modifica la vista de inicio de sesión para regresar los dos tokens y Define una vista para refrescar el token."],"metadata":{"id":"kIyGXI0R0Vkk"}},{"cell_type":"code","source":["# Modificamos accounts/views.py\n","\n","# Importa la biblioteca PyJWT, utilizada para decodificar y verificar tokens JWT.\n","import jwt\n","# Importa las configuraciones de Django para acceder a variables como SECRET_KEY.\n","from django.conf import settings\n","# Importa la función de autenticación de Django, aunque no se utiliza directamente en este fragmento.\n","from django.contrib.auth import authenticate\n","# Importa el modelo de usuario predeterminado de Django.\n","from django.contrib.auth.models import User\n","# Importa el módulo status que contiene códigos de estado HTTP para las respuestas.\n","from rest_framework import status\n","# Importa la clase Request de DRF que encapsula la solicitud HTTP.\n","from rest_framework.request import Request\n","# Importa la clase Response que se utiliza para retornar respuestas desde las vistas de DRF.\n","from rest_framework.response import Response\n","# Importa APIView, la clase base para todas las vistas en DRF.\n","from rest_framework.views import APIView\n","\n","# Importa LoginSerializer y RefreshTokenSerializer\n","from .serializers import LoginSerializer, RefreshTokenSerializer\n","# Importa la función generate_jwt_token\n","from .utils import generate_jwt_token\n","\n","\n","# Define una vista LoginViews\n","class LoginView(APIView):\n","    # Establece LoginSerializer como el serializador por defecto para esta vista.\n","    serializer_class = LoginSerializer\n","\n","    # Define cómo manejar POST, para enviar datos de inicio de sesión.\n","    def post(self, request):\n","        # Crea una instancia de LoginSerializer con los datos enviados en la solicitud.\n","        serializer = LoginSerializer(data=request.data)\n","        # Verifica si los datos son válidos.\n","        if serializer.is_valid():\n","            # Obtiene el nombre de usuario de los datos validados.\n","            username = serializer.validated_data[\"username\"]\n","            # Obtiene la contraseña de los datos validados.\n","            password = serializer.validated_data[\"password\"]\n","            # Intenta autenticar al usuario utilizando la función authenticate.\n","            user = authenticate(request, username=username, password=password)\n","            # Verifica si la autenticación fue exitosa.\n","            if user is not None:\n","                # Genera los dos tokens JWT para el usuario\n","                access_token, refresh_token = generate_jwt_tokens(user)\n","                # Retorna una respuesta indicando el éxito de la operación.\n","                return Response(\n","                    {\n","                        \"status\": \"OK\",\n","                        \"access_token\": access_token,\n","                        \"refresh_token\": refresh_token,\n","                    },\n","                    status=status.HTTP_200_OK,\n","                )\n","            # Retorna una respuesta indicando el fallo de la operación.\n","            return Response(\n","                {\n","                    \"status\": \"UNAUTHORIZED\",\n","                    \"data\": \"The username or password you are trying to log in\"\n","                    \" with are incorrect.\",\n","                },\n","                status=status.HTTP_401_UNAUTHORIZED,\n","            )\n","        # Retorna una respuesta indicando el fallo de la operación.\n","        return Response(\n","            {\"status\": \"BAD_REQUEST\", \"data\": serializer.errors},\n","            status=status.HTTP_400_BAD_REQUEST,\n","        )\n","\n","\n","# # Define una vista LoginViews\n","class RefreshTokenView(APIView):\n","    # Establece RefreshTokenSerializer como el serializador por defecto para esta vista.\n","    serializer_class = RefreshTokenSerializer\n","\n","    # Define cómo manejar POST, para enviar datos de refresco de token.\n","    def post(self, request: Request):\n","        # Crea una instancia del serializador con los datos proporcionados en la solicitud.\n","        serializer = RefreshTokenSerializer(data=request.data)\n","        # Verifica si los datos son válidos.\n","        if serializer.is_valid():\n","            # Obtiene el refresh token validado desde los datos del serializador.\n","            refresh_token = serializer.validated_data[\"refresh_token\"]\n","            try:\n","                # Intenta decodificar el refresh token utilizando la clave secreta y el algoritmo especificado.\n","                payload = jwt.decode(\n","                    refresh_token,\n","                    f\"refresh.{settings.SECRET_KEY}\",\n","                    algorithms=[\"HS256\"],\n","                )\n","                # Extrae el nombre de usuario del payload del token.\n","                username = payload[\"username\"]\n","                # Busca el usuario en la base de datos por su nombre natural (nombre de usuario).\n","                user = User.objects.get_by_natural_key(username)\n","                # Verifica si se encontró el usuario.\n","                if user is not None:\n","                    # Genera nuevos tokens de acceso y actualización.\n","                    access_token, refresh_token = generate_jwt_tokens(user)\n","                    # Devuelve una respuesta con los nuevos tokens.\n","                    return Response(\n","                        {\n","                            \"status\": \"OK\",\n","                            \"access_token\": access_token,\n","                            \"refresh_token\": refresh_token,\n","                        },\n","                        status=status.HTTP_200_OK,\n","                    )\n","            # Captura el error si el token ha expirado y devuelve un mensaje apropiado.\n","            except jwt.ExpiredSignatureError:\n","                return Response(\n","                    {\n","                        \"status\": \"OK\",\n","                        \"data\": \"The token has expired. Please log in again to\"\n","                        \" obtain a new token.\",\n","                    },\n","                    status=status.HTTP_401_UNAUTHORIZED,\n","                )\n","            # Captura el error si el token es inválido y devuelve un mensaje apropiado.\n","            except jwt.InvalidTokenError:\n","                return Response(\n","                    {\n","                        \"status\": \"OK\",\n","                        \"data\": \"The provided token is invalid. Please check \"\n","                        \"the token and try again.\",\n","                    },\n","                    status=status.HTTP_401_UNAUTHORIZED,\n","                )\n","        # Si los datos iniciales no son válidos o si ocurre cualquier otro error, devuelve un error de solicitud incorrecta con detalles.\n","        return Response(\n","            {\"status\": \"BAD_REQUEST\", \"data\": serializer.errors},\n","            status=status.HTTP_400_BAD_REQUEST,\n","        )"],"metadata":{"id":"fhuV547P0WAh"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["### Asigna URLs específicas a vistas nuevas definidas en la aplicación."],"metadata":{"id":"ReNiiWTq2fWR"}},{"cell_type":"code","source":["# Creamos accounts/urls.py\n","\n","# Importa las vistas LoginView.\n","from .views import LoginView\n","\n","# Cada elemento de esta lista es una llamada a la función path, que define una asociación entre un patrón de URL y una vista.\n","urlpatterns = [\n","    path(\"login/\", LoginView.as_view()),\n","    path(\"token/refresh/\", RefreshTokenView.as_view()),\n","]"],"metadata":{"id":"gxuF_QgG2kPF"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["### Modificamos middleware para agregar las rutas de URL para las cuales no debe aplicar la lógica de autenticación JWT."],"metadata":{"id":"7ch5eJlh2zS7"}},{"cell_type":"code","source":["# Modificamos accounts/middlewares.py\n","\n","# Define la función jwt_auth_middleware, que acepta un argumento get_response.\n","# get_response es una función de callback que Django llama con el objeto request para obtener la respuesta de la vista o del siguiente middleware.\n","def jwt_auth_middleware(get_response):\n","    # Define una lista de endpoints llamada excluded_endpoints que contiene las rutas de URL para las cuales este middleware no debe aplicar la lógica de autenticación JWT.\n","    excluded_endpoints = [\"/login/\", \"/token/refresh/\"]\n","\n","    def middleware(request: Request):\n","        # Este bloque condicional verifica si la ruta de la solicitud actual está en la lista de excluded_endpoints.\n","        # Si es así, el middleware permite que la solicitud continúe sin procesamiento adicional.\n","        if request.path in excluded_endpoints:\n","            return get_response(request)\n","\n","        # Comprueba si la cabecera HTTP Authorization está presente en la solicitud.\n","        if \"HTTP_AUTHORIZATION\" in request.META:\n","            # Extrae el valor de la cabecera Authorization.\n","            auth_header = request.META[\"HTTP_AUTHORIZATION\"]"],"metadata":{"id":"HQEcAwTF218O"},"execution_count":null,"outputs":[]}]}