tutoriales.com

Gestionando Sesiones y Estado de Usuario en Aplicaciones Serverless con Cognito y Lambda

Este tutorial te guiará a través de la implementación de un sistema robusto para gestionar sesiones de usuario y mantener el estado en aplicaciones serverless, utilizando AWS Cognito para la autenticación y AWS Lambda para la lógica de backend. Descubrirás cómo configurar piscinas de usuarios, manejar flujos de autenticación y asegurar tus APIs.

Intermedio20 min de lectura6 views
Reportar error

La arquitectura serverless ha revolucionado la forma en que construimos aplicaciones, permitiendo escalar automáticamente y pagar solo por el consumo. Sin embargo, gestionar el estado del usuario y las sesiones puede presentar desafíos únicos en un entorno sin estado. Este tutorial abordará cómo superar estos retos utilizando dos servicios fundamentales de AWS: Amazon Cognito y AWS Lambda.

🚀 Introducción a la Gestión de Estado Serverless

En una aplicación tradicional, el estado de la sesión se gestiona a menudo en el servidor, utilizando cookies o bases de datos de sesión. En el mundo serverless, donde cada invocación de una función Lambda es una instancia independiente y efímera, necesitamos un enfoque diferente. Aquí es donde entran en juego servicios como Amazon Cognito, que actúan como una capa de autenticación y gestión de identidad centralizada, y AWS Lambda, que ejecuta nuestra lógica de negocio sin preocuparnos por los servidores subyacentes.

¿Por qué Cognito y Lambda para Sesiones Serverless? 🤔

Amazon Cognito ofrece un servicio de identidad completamente gestionado que simplifica la autenticación, autorización y gestión de usuarios para tus aplicaciones web y móviles. Se integra perfectamente con otras soluciones de AWS, incluyendo Lambda y API Gateway, lo que lo convierte en una elección ideal para arquitecturas serverless.

AWS Lambda, por su parte, nos permite ejecutar código en respuesta a eventos sin aprovisionar ni administrar servidores. Al combinarlo con Cognito, podemos construir un backend de autenticación flexible y escalable, donde Lambda puede manejar la lógica personalizada de registro, inicio de sesión y gestión de perfiles de usuario.

💡 Consejo: Considera el uso de Cognito para cualquier aplicación que requiera autenticación de usuarios. Reduce drásticamente la complejidad y el tiempo de desarrollo.

🛠️ Prerequisitos y Configuración Inicial

Antes de sumergirnos en la implementación, asegúrate de tener lo siguiente:

  • Una cuenta de AWS activa.
  • AWS CLI configurado con las credenciales adecuadas.
  • Conocimientos básicos de AWS Lambda, Amazon Cognito y Node.js (o Python, si lo prefieres para las funciones Lambda).

Configuración del Entorno de Desarrollo 💻

Si aún no lo tienes, puedes instalar el AWS CLI:

curl "https://awscli.amazonaws.com/awscli-bundle.zip" -o "awscli-bundle.zip"
unzip awscli-bundle.zip
sudo ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws
aws --version

👤 Creando un Pool de Usuarios de Amazon Cognito

El primer paso es configurar un Pool de Usuarios de Cognito, que será nuestro directorio de usuarios. Aquí es donde se almacenarán las credenciales de los usuarios, se gestionará el registro, el inicio de sesión y la recuperación de contraseñas.

Paso a Paso: Creación del User Pool en la Consola AWS

  1. Navega a Amazon Cognito: En la consola de AWS, busca y selecciona "Cognito".
  2. Crea un Pool de Usuarios: Haz clic en "Manage User Pools" y luego en "Create a user pool".
  3. Configura los atributos de inicio de sesión: Dale un nombre a tu Pool de Usuarios (ej. MyAppUserPool). En la sección "How do you want your end users to sign in?", elige Username o Email. Para este tutorial, usaremos Email.
  4. Define los atributos estándar y personalizados: Puedes mantener los atributos estándar por defecto (email, phone number, etc.). Si necesitas información adicional, puedes añadir atributos personalizados.
  5. Configura políticas de contraseña: Define la complejidad de las contraseñas (longitud mínima, caracteres especiales, etc.).
  6. Configura la verificación de MFA (opcional): Para mayor seguridad, puedes habilitar la autenticación multifactor. Por ahora, podemos dejarlo en "No MFA".
  7. Personaliza mensajes: Modifica las plantillas de email/SMS para la verificación de cuenta y recuperación de contraseña.
  8. Añade un cliente de aplicación: Necesitarás un "App client" para permitir que tu aplicación interactúe con el Pool de Usuarios. Ve a "App clients" y haz clic en "Add an app client". Dale un nombre (ej. WebAppClient). Desmarca "Generate client secret" si tu aplicación es de tipo web o móvil (ya que el secreto se expondría).
  9. Revisa y crea: Revisa todas tus configuraciones y haz clic en "Create pool".
📌 Nota: Guarda el ID del Pool de Usuarios y el ID del App Client. Los necesitarás más adelante.
1. Iniciar en Cognito 2. Crear User Pool 3. Configurar Atributos (Email) 4. Definir Políticas de Contraseña 5. Añadir App Client 6. Crear Pool

Configuración con AWS CLI (Opcional) 🤖

Para aquellos que prefieren la línea de comandos, aquí hay un ejemplo básico para crear un User Pool:

aws cognito-idp create-user-pool --pool-name MyAppUserPoolCLI \
--auto-verified-attributes email \
--schema Name=email,Mutable=true,Required=true,AttributeDataType=String \
--policies PasswordPolicy={MinimumLength=8,RequireUppercase=true,RequireLowercase=true,RequireNumbers=true,RequireSymbols=true}

# Guardar el ID del Pool de Usuarios (sustituir el ARN real)
USER_POOL_ID="us-east-1_XXXXxxxxx"

aws cognito-idp create-user-pool-client --user-pool-id $USER_POOL_ID \
--client-name WebAppClientCLI --no-generate-secret

🔐 Flujos de Autenticación con Cognito

Cognito soporta varios flujos de autenticación. Para aplicaciones web y móviles, los flujos estándar de registro, confirmación de registro e inicio de sesión son los más comunes.

Registro de Usuario (Sign Up) 📝

Un usuario envía su email y contraseña a Cognito. Cognito crea una entrada de usuario y, opcionalmente, envía un código de verificación al email.

Flujo: Aplicación -> Cognito (SignUp) -> Email/SMS de Verificación

Confirmación de Usuario (Confirm Sign Up) ✅

El usuario introduce el código de verificación recibido para activar su cuenta.

Flujo: Aplicación -> Cognito (ConfirmSignUp con código)

Inicio de Sesión (Sign In) 🚪

El usuario proporciona sus credenciales. Cognito las valida y, si son correctas, devuelve tokens (ID Token, Access Token, Refresh Token).

Flujo: Aplicación -> Cognito (SignIn con credenciales) -> Tokens (ID, Access, Refresh)

60% de autenticación cubierta

🤝 Integrando Cognito con AWS Lambda y API Gateway

Ahora vamos a ver cómo podemos usar Lambda para interactuar con Cognito y cómo API Gateway puede proteger nuestras APIs utilizando los tokens de Cognito.

1. Función Lambda para Registro Personalizado (Opcional) ✨

Aunque Cognito maneja el registro por defecto, puedes usar Lambda Triggers para personalizar el proceso. Por ejemplo, para añadir el usuario a una base de datos después del registro, o para realizar validaciones adicionales.

Ejemplo de un trigger Pre Sign-up:

Esta función se ejecutaría antes de que Cognito cree el usuario. Podrías, por ejemplo, verificar si el email ya existe en una base de datos externa.

// lambda_pre_signup.js
exports.handler = async (event) => {
    // Log the event for debugging
    console.log('Pre Sign-up Trigger Event:', JSON.stringify(event, null, 2));

    // Check if the email domain is allowed
    const email = event.request.userAttributes.email;
    if (!email.endsWith('@example.com')) {
        throw new Error('Solo se permiten emails del dominio @example.com');
    }

    // You can also add custom attributes or modify existing ones
    // event.response.autoConfirmUser = true; // Auto-confirm user if needed
    // event.response.autoVerifyEmail = true; // Auto-verify email if needed

    // Return the event to allow Cognito to proceed
    return event;
};

Pasos para configurar el trigger:

  1. Crea una nueva función Lambda con el código anterior.
  2. En la configuración de tu Pool de Usuarios de Cognito, ve a "Triggers".
  3. En la sección "Pre sign-up", selecciona tu función Lambda recién creada.

2. Protegiendo APIs con un Autorizador Cognito de API Gateway 🔒

Una vez que un usuario inicia sesión y recibe un ID Token de Cognito, este token puede usarse para autenticar solicitudes a tus APIs alojadas en API Gateway.

Pasos:

  1. Crea una API en API Gateway: Si no tienes una, crea una nueva API REST (o HTTP API, el proceso es similar).

  2. Crea un Autorizador Cognito:

    • En tu API Gateway, ve a "Autorizadores".
    • Haz clic en "Crear nuevo autorizador".
    • Dale un nombre (ej. CognitoAuthorizer).
    • Tipo: Cognito.
    • Pool de usuarios de Cognito: Selecciona tu MyAppUserPool.
    • Fuente de tokens: Authorization (este es el encabezado donde tu aplicación enviará el ID Token).
    • Haz clic en "Crear".
  3. Aplica el Autorizador a un Recurso/Método:

    • Navega al recurso y método (ej. /items - GET) que deseas proteger.
    • Haz clic en "Acción" -> "Adjuntar autorizador".
    • Selecciona tu CognitoAuthorizer.
    • Haz clic en "Adjuntar autorizador".
  4. Despliega la API: Para que los cambios surtan efecto, debes desplegar tu API en una etapa.

Ahora, cualquier solicitud a ese endpoint sin un ID Token válido en el encabezado Authorization: Bearer <ID_TOKEN> será rechazada por API Gateway.

Login (Cognito) Identity Provider Cliente (App) 1. Login 2. Devuelve ID Token API Gateway (Autorizador Cognito) 3. API Request + ID Token Header Lambda (Backend) 4. Validado Validación Interna de Token

3. Función Lambda de Backend para Lógica Protegida 🌐

Esta es la función Lambda que ejecuta tu lógica de negocio y solo se invoca si el usuario está autenticado.

// lambda_protected_resource.js
exports.handler = async (event) => {
    // El evento contiene información del autorizador si el token fue válido
    console.log('Evento de recurso protegido:', JSON.stringify(event, null, 2));

    // Puedes acceder a la información del usuario desde el ID Token decodificado
    // event.requestContext.authorizer.claims contiene los claims del ID Token
    const userId = event.requestContext.authorizer.claims.sub;
    const userEmail = event.requestContext.authorizer.claims.email;

    return {
        statusCode: 200,
        headers: {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Origin': '*'
        },
        body: JSON.stringify({
            message: `Hola ${userEmail}, tu ID de usuario es ${userId}. Has accedido a un recurso protegido!`,
            data: {
                // Algún dato que tu API deba devolver
                items: ['item1', 'item2', 'item3']
            }
        }),
    };
};
🔥 Importante: Siempre valida y autoriza la información del usuario en tu función Lambda, incluso si API Gateway ya ha validado el token. Esto añade una capa extra de seguridad.

🔄 Gestión de Tokens y Renovación de Sesión

Los tokens de Cognito tienen una vida útil limitada. El ID Token y el Access Token suelen expirar en una hora. El Refresh Token tiene una vida útil más larga (hasta 30 días o más, configurable).

¿Cómo funcionan los tokens? 🪙

  • ID Token: Contiene información sobre el usuario autenticado (claims). Se usa para verificar la identidad del usuario.
  • Access Token: Se usa para autorizar el acceso a recursos protegidos (ej. API Gateway, AWS API). Contiene los scopes de autorización.
  • Refresh Token: Se usa para obtener nuevos ID y Access Tokens sin que el usuario tenga que iniciar sesión de nuevo.

Renovando Sesiones ♻️

Tu aplicación cliente (web o móvil) debe almacenar el Refresh Token de forma segura (por ejemplo, en localStorage o sessionStorage para web, o almacenamiento seguro específico de la plataforma para móvil). Cuando el ID Token o Access Token expiran, la aplicación puede usar el Refresh Token para solicitar nuevos tokens a Cognito sin la interacción del usuario.

La mayoría de las bibliotecas cliente de Cognito (como aws-amplify) manejan esta renovación de tokens automáticamente.

Ejemplo de uso de `aws-amplify` para autenticación

AWS Amplify es una suite de herramientas que facilita la integración con servicios AWS, incluyendo Cognito. Su librería JS es muy popular para aplicaciones web y móviles.

import { Amplify, Auth } from 'aws-amplify';

Amplify.configure({
    Auth: {
        region: 'us-east-1',
        userPoolId: 'us-east-1_XXXXxxxxx', // Tu User Pool ID
        userPoolWebClientId: 'YYYYYYYYYYYYYYYYYYYYYYYYY' // Tu App Client ID
    }
});

async function signUp() {
    try {
        const { user } = await Auth.signUp({
            username: 'nuevo_usuario@example.com',
            password: 'Password123!',
            attributes: {
                email: 'nuevo_usuario@example.com'
            }
        });
        console.log(user);
    } catch (error) {
        console.error('Error signing up:', error);
    }
}

async function confirmSignUp() {
    try {
        await Auth.confirmSignUp('nuevo_usuario@example.com', '123456'); // Código de verificación
        console.log('Usuario confirmado');
    } catch (error) {
        console.error('Error confirming sign up:', error);
    }
}

async function signIn() {
    try {
        const user = await Auth.signIn('nuevo_usuario@example.com', 'Password123!');
        console.log('Usuario logueado:', user);
        // Los tokens están en user.signInUserSession
        const idToken = user.signInUserSession.idToken.jwtToken;
        console.log('ID Token:', idToken);
    } catch (error) {
        console.error('Error signing in:', error);
    }
}

async function callProtectedApi() {
    try {
        const session = await Auth.currentSession();
        const idToken = session.getIdToken().getJwtToken();

        const response = await fetch('TU_URL_API_PROTEGIDA',
        {
            headers: {
                Authorization: `Bearer ${idToken}`
            }
        });
        const data = await response.json();
        console.log('Respuesta de API protegida:', data);
    } catch (error) {
        console.error('Error al llamar a la API protegida:', error);
    }
}

// Ejemplo de uso
// signUp();
// confirmSignUp();
// signIn();
// callProtectedApi();


📊 Monitoreo y Observabilidad

Monitorear tus componentes serverless es crucial. AWS CloudWatch te permite observar tus Lambdas y Cognito.

  • CloudWatch Logs: Revisa los logs de tus funciones Lambda para depuración.
  • CloudWatch Metrics: Observa métricas como el número de invocaciones de Lambda, errores, latencia, y métricas de Cognito (registros de usuarios, intentos de inicio de sesión).
  • AWS X-Ray: Para rastrear las solicitudes a medida que atraviesan tus servicios serverless (API Gateway -> Lambda -> DynamoDB, etc.).

⚖️ Ventajas y Desafíos de la Gestión de Estado Serverless

Ventajas ✅

  • Escalabilidad automática: Cognito y Lambda escalan bajo demanda para manejar millones de usuarios y solicitudes.
  • Reducción de costos: Pagas solo por el uso real, sin costos fijos de servidores de autenticación.
  • Mantenimiento mínimo: AWS gestiona la infraestructura subyacente, liberando a tu equipo para centrarse en la lógica de negocio.
  • Seguridad integrada: Cognito ofrece características de seguridad robustas, como MFA, detección de riesgos y cumplimiento de estándares.

Desafíos ⚠️

  • Complejidad inicial: La curva de aprendizaje de Cognito y la integración con Lambda/API Gateway puede ser un poco pronunciada al principio.
  • Gestión de tokens: Necesidad de implementar la lógica de almacenamiento y renovación de tokens en el cliente.
  • Frías (Cold Starts) de Lambda: Aunque minimizadas, las funciones Lambda pueden experimentar latencia inicial ocasional.
  • Debugging: Puede ser más complejo depurar en un entorno distribuido y sin estado.
CaracterísticaArquitectura TradicionalArquitectura Serverless (Cognito + Lambda)
Gestión de ServidoresManualGestionado por AWS
EscalabilidadManual/Auto-escalado configuradoAutomática y elástica
AutenticaciónServidor de autenticación propioCognito (servidor de identidad gestionado)
Estado de SesiónServidor con bases de datos de sesiónTokens (JWT) gestionados en cliente, Cognito
CostosFijos + variablesBasados en consumo (pago por uso)
MantenimientoAltoBajo

🎯 Buenas Prácticas y Consejos Avanzados

  • Siempre usar HTTPS: Asegura que todas las comunicaciones con Cognito y tus APIs estén cifradas.
  • Políticas de menor privilegio: Otorga a tus funciones Lambda solo los permisos que necesitan para interactuar con Cognito y otros servicios.
  • Auditoría con CloudTrail: Habilita CloudTrail para registrar todas las llamadas a la API de AWS, lo que te permite auditar las actividades de seguridad.
  • Manejo de errores: Implementa una robusta gestión de errores y reintentos en tus funciones Lambda y en tu aplicación cliente.
  • Versionado de Lambda: Usa el versionado de Lambda para gestionar los cambios en tu código y desplegar sin interrupciones.
  • Pruebas unitarias e integración: Asegúrate de probar exhaustivamente tus funciones Lambda y los flujos de autenticación.
  • Personalización de interfaz de usuario (UI): Cognito ofrece una UI alojada para la autenticación, lo que puede simplificar el desarrollo, aunque también puedes construir tu propia UI si necesitas más control.
Define Requisitos: ¿Qué tipo de autenticación necesitas (registro, redes sociales)?
Configura Cognito: Crea el User Pool, App Client, atributos y políticas de contraseña.
Implementa Lógica Lambda: Desarrolla funciones para triggers o backend protegido.
Integra API Gateway: Protege tus APIs con el autorizador Cognito.
Desarrolla Cliente: Usa AWS Amplify o SDKs para la integración en tu app.
Monitorea y Optimiza: Usa CloudWatch y X-Ray para observar el rendimiento y seguridad.

🔚 Conclusión

La gestión de sesiones y el estado de usuario en arquitecturas serverless es un pilar fundamental para construir aplicaciones modernas, escalables y seguras. Al aprovechar Amazon Cognito para la autenticación y AWS Lambda para la lógica de negocio, puedes crear un sistema robusto que maneja de manera eficiente la identidad del usuario sin la complejidad de la gestión de servidores.

Este tutorial ha cubierto desde la configuración básica de Cognito hasta la integración con API Gateway y Lambda, proporcionándote una base sólida para implementar tus propias soluciones serverless. Recuerda que la seguridad y la experiencia del usuario deben ser siempre tus prioridades al diseñar estos sistemas.

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!