La creación de aplicaciones seguras es uno de los aspectos más importantes cuando se desarrollan sistemas basados en la web. El proceso de registrar usuarios, verificar sus credenciales y gestionar el acceso adecuado es fundamental para proteger tanto la información sensible como la integridad del sistema. En este contexto, FastAPI ofrece una excelente plataforma para construir aplicaciones web rápidas y seguras, pero requiere una configuración adecuada en términos de autenticación y autorización. A continuación, se describe cómo establecer un sistema de registro de usuarios y otros mecanismos de seguridad clave.

El primer paso para asegurar una aplicación FastAPI es configurar el registro de usuarios. Este proceso implica la recolección de detalles del usuario, como el nombre de usuario, el correo electrónico y la contraseña, y almacenarlos de forma segura en una base de datos. Para ello, se utilizará un algoritmo de hashing (bcrypt) que convierte la contraseña en un formato irreversible, protegiéndola contra accesos no autorizados. Esto no solo es una buena práctica, sino que también ayuda a cumplir con las regulaciones de seguridad y privacidad, como el GDPR.

En cuanto a la base de datos, se puede utilizar SQLAlchemy, un ORM (Object-Relational Mapper) de Python, para interactuar con una base de datos SQL como SQLite. La configuración de la base de datos comienza con la creación de una clase de usuario que mapea la tabla de usuarios. La tabla debe contener campos como id, username, email y hashed_password para almacenar la información del usuario de manera segura.

El siguiente paso es definir una función que se encargue de agregar usuarios a la base de datos. Esta función toma como entrada el nombre de usuario, la contraseña y el correo electrónico, y luego utiliza el contexto del paquete passlib para generar un hash de la contraseña. Si el nombre de usuario o el correo electrónico ya existen en la base de datos, se debe gestionar la excepción IntegrityError, que permite manejar la duplicación de datos y evitar la inserción de información conflictiva.

Una vez que los datos se han almacenado de manera segura, el siguiente componente es crear un endpoint de FastAPI que permita a los usuarios registrarse. Este endpoint acepta una solicitud POST con los datos del usuario en formato JSON y devuelve un mensaje de éxito si el usuario se registra correctamente. Si ya existe un usuario con el mismo nombre o correo, el servidor responderá con un código de error HTTP 409 (conflicto), indicando que la información ya está en uso.

Sin embargo, el registro de usuarios es solo el primer paso. Una vez que los usuarios están registrados, es necesario implementar un sistema de autenticación robusto para verificar sus credenciales y asegurar que solo los usuarios autorizados puedan acceder a las funcionalidades protegidas de la aplicación. FastAPI facilita esta tarea mediante el uso de OAuth2 y JWT (JSON Web Token), dos estándares ampliamente utilizados en la industria para gestionar sesiones de usuario y control de acceso.

OAuth2, combinado con JWT, proporciona un sistema flexible para autenticar a los usuarios sin necesidad de mantener una sesión en el servidor. En este enfoque, cuando un usuario inicia sesión con sus credenciales, se le otorga un token JWT que incluye su identidad y, opcionalmente, su rol. Este token se puede almacenar en el cliente, y en cada solicitud subsiguiente, el cliente incluirá el token en la cabecera de la solicitud para validar su identidad.

Además, es recomendable implementar el control de acceso basado en roles (RBAC). Este mecanismo permite asignar diferentes permisos a los usuarios según su rol dentro del sistema, lo que proporciona una capa adicional de seguridad. Por ejemplo, un usuario común podría tener acceso solo a recursos básicos, mientras que un administrador podría gestionar configuraciones del sistema o acceder a información confidencial.

Otra característica importante es la autenticación de terceros. Utilizar servicios de inicio de sesión externo, como GitHub o Google, puede simplificar el proceso de autenticación para el usuario final. Al integrar estos servicios, los usuarios pueden iniciar sesión sin necesidad de crear una cuenta nueva, lo que mejora la experiencia del usuario y reduce las barreras de entrada.

Para mejorar aún más la seguridad, se debe implementar la autenticación de múltiples factores (MFA). Este proceso añade una capa extra de protección al requerir que el usuario proporcione algo más que solo su contraseña para acceder al sistema. Generalmente, esto implica un código enviado al teléfono móvil o generado por una aplicación de autenticación. Esto asegura que incluso si un atacante obtiene las credenciales de un usuario, no podrá acceder a la cuenta sin también tener acceso al segundo factor.

Además de todo lo mencionado, se debe garantizar que las sesiones de usuario sean gestionadas de manera eficiente. Esto implica manejar correctamente las cookies de sesión y proporcionar un mecanismo adecuado para que los usuarios puedan cerrar sesión de manera segura. Un cierre de sesión bien implementado asegura que las credenciales de un usuario no permanezcan activas en el cliente después de que haya terminado su sesión.

Es crucial también considerar los aspectos de escalabilidad y mantenimiento a largo plazo del sistema. Por ejemplo, al manejar sesiones y roles de usuarios, es recomendable tener un sistema de registro de auditoría que permita hacer un seguimiento de las acciones de los usuarios, lo que facilita la identificación de posibles problemas o accesos no autorizados. Además, la integración de sistemas como Redis para la gestión de sesiones puede mejorar el rendimiento y la capacidad de respuesta de la aplicación en entornos con gran volumen de usuarios.

En resumen, configurar un sistema de autenticación y autorización adecuado en una aplicación FastAPI es fundamental para garantizar la seguridad y privacidad de los usuarios. Desde la implementación de un registro de usuarios básico hasta la integración de protocolos avanzados como OAuth2, JWT y MFA, cada componente juega un papel vital en la protección del sistema. La combinación de estas técnicas asegurará que solo los usuarios autorizados puedan acceder a los recursos protegidos y reducirá el riesgo de ataques.

¿Cómo implementar la inyección de dependencias en FastAPI?

La inyección de dependencias en FastAPI es una técnica fundamental que permite la creación de aplicaciones modulares, mantenibles y fáciles de probar. Esta funcionalidad, integrada de manera fluida con el sistema de anotaciones de Python, permite gestionar de forma eficiente los parámetros de las funciones de un endpoint, como las consultas, el cuerpo o los parámetros de la ruta.

En FastAPI, la inyección de dependencias se maneja principalmente a través de la clase Depends. Esta clase puede ser utilizada para declarar dependencias de cualquier tipo de función o servicio que necesite el endpoint para ejecutar su lógica. Lo interesante de esta aproximación es que la función dependiente se ejecuta antes de la lógica del endpoint, lo que garantiza que todos los parámetros estén listos cuando el controlador realice su trabajo. A continuación se detalla cómo funciona este proceso.

Por ejemplo, consideremos un caso en el que queremos definir un endpoint que reciba un rango de fechas como parámetros de consulta. En lugar de incluir la lógica de validación y el manejo de esos parámetros directamente en el endpoint, se puede definir una función dependiente que se encargue de resolver este rango de fechas. Esta función se podría ver así:

python
from datetime import date, timedelta
from fastapi import Query, Depends def time_range( start: date = Query(None, example=date.today()), end: date | None = Query(None, example=date.today() + timedelta(days=7)), ) -> Tuple[date, date | None]: # Validación de fechas if start and end and start > end: raise ValueError("La fecha de inicio no puede ser posterior a la fecha de fin.") return start, end

Aquí, la función time_range es la que se encarga de recibir y devolver el rango de fechas. Cuando esta función es utilizada como una dependencia, no es necesario que el endpoint maneje explícitamente estos parámetros; solo se deben definir como parámetros de la función que los recibe a través de la clase Depends:

python
from fastapi import FastAPI, Depends app = FastAPI() @app.get("/v1/trips") def get_trips(time_range: Depends(time_range)): start, end = time_range
return {"message": f"Solicitando viajes desde {start} hasta {end}"}

Cuando se realiza una solicitud a este endpoint, FastAPI resolverá automáticamente los valores para start y end, proporcionando así una forma sencilla y elegante de manejar parámetros comunes en varios puntos de la aplicación.

Inyección de dependencias en pruebas

Una de las ventajas más importantes de la inyección de dependencias es su capacidad para facilitar la escritura de pruebas. Gracias a la capacidad de sobrescribir dependencias, es posible simular servicios o parámetros sin necesidad de acceder a la base de datos o ejecutar operaciones costosas.

Para realizar una prueba de un endpoint que dependa de una función como time_range, se puede sobrescribir la dependencia en el entorno de prueba mediante la configuración de app.dependency_overrides. De esta manera, podemos definir qué valores se deben inyectar en lugar de los valores por defecto:

python
from datetime import date from fastapi.testclient import TestClient from app.main import app def test_get_trips(): client = TestClient(app)
app.dependency_overrides[time_range] = lambda: (date(2024, 2, 1), None)
response = client.get(
"/v1/trips") assert response.json() == {"message": "Solicitando viajes desde 2024-02-01 hasta None"}

Esta técnica es muy útil cuando se escriben pruebas que no deben afectar la base de datos de producción, lo que permite realizar pruebas unitarias o de integración sin riesgos. Además, la posibilidad de simular o "mockear" funciones costosas o complejas mejora la calidad de las pruebas al hacerlas más predecibles y enfocadas.

Sub-dependencias y modularidad

FastAPI también permite trabajar con sub-dependencias, lo que brinda aún más modularidad al código. Por ejemplo, en lugar de manejar varios parámetros directamente en un endpoint, podemos crear dependencias anidadas que se encarguen de la validación y procesamiento de cada uno de esos parámetros.

Imaginemos que estamos desarrollando una API que maneja diferentes tipos de viajes: cruceros, escapadas urbanas y estancias en resorts. Podemos crear una dependencia para seleccionar el tipo de viaje y, a continuación, utilizar esa dependencia dentro de otra función que verifique la validez de un cupón de descuento:

python
from fastapi import Query, Path, Depends
from typing import Annotated def select_category( category: Annotated[str, Path(description="Tipo de viaje al que estás interesado", enum=["Cruceros", "Escapadas Urbanas", "Estancias en Resorts"])] ) -> str: return category def check_coupon_validity( category: Annotated[str, Depends(select_category)], code: str | None = Query(None, description="Código de cupón") ) -> bool: coupon_dict = { "cruceros": "CRUISE10", "escapadas-urbanas": "CITYBREAK15", "estancias-en-resorts": "RESORT20", } return coupon_dict.get(category) == code

En este caso, la función check_coupon_validity depende de select_category, lo que permite combinar lógicamente dos dependencias. A continuación, el endpoint que consulta los viajes se ve de la siguiente forma:

python
@app.get("/v2/trips/{category}")
def get_trips_by_category( category: Annotated[str, Depends(select_category)], discount_applicable: Annotated[bool, Depends(check_coupon_validity)] ): message = f"Has solicitado viajes de tipo {category}." if discount_applicable: message += " El código de cupón es válido. ¡Obtendrás un descuento!" return message

Este enfoque no solo mejora la legibilidad y mantenimiento del código, sino que también facilita la reutilización de las dependencias en diferentes puntos de la aplicación.

Consideraciones adicionales

Es importante entender que la inyección de dependencias en FastAPI no se limita a la simple manipulación de parámetros. Esta técnica es especialmente poderosa en aplicaciones grandes y complejas, donde es fundamental mantener un código limpio y bien organizado. Además, la capacidad de sobrescribir dependencias facilita la personalización del comportamiento de la aplicación según el entorno (por ejemplo, en pruebas) sin necesidad de cambiar el código fuente de los endpoints.

Otro aspecto que debe considerarse es la posibilidad de manejar dependencias tanto síncronas como asíncronas. FastAPI puede gestionar automáticamente ambas tipos de funciones, permitiendo que el sistema sea flexible y eficiente sin necesidad de intervención manual.

Finalmente, el uso de dependencias, sub-dependencias y la capacidad de sobrescribir dependencias facilita la integración con otros servicios, bases de datos o mecanismos de autenticación, lo que convierte a FastAPI en una herramienta extremadamente poderosa para desarrollar aplicaciones web modernas.