Autenticación Multi-Tenant con Amazon Cognito

La autenticación es solo una parte del problema en una aplicación SaaS multi-tenant. Después de identificar al usuario, cada solicitud de API también debe vincularse al tenant correcto y estar autorizada para las acciones que ese usuario puede realizar.

Amazon Cognito puede encargarse del inicio de sesión y de la emisión de tokens, pero el aislamiento de tenants todavía debe diseñarse explícitamente. El patrón que se describe a continuación mantiene la autenticación en Cognito, a la vez que aplica los límites entre tenants en las capas de aplicación y de datos.

Arquitectura multi-tenant de Cognito

Qué debe hacer Cognito

Un User Pool de Cognito administra a los usuarios y los autentica. Después de un inicio de sesión exitoso, Cognito emite JSON Web Tokens (JWTs), que son tokens firmados que contienen claims sobre la identidad autenticada.

Para una aplicación multi-tenant, la pregunta importante es: ¿a qué tenant pertenece esta identidad?

Un modelo práctico es mantener un identificador de tenant como:

tenant_123

El registro del usuario puede almacenar ese identificador como un atributo personalizado de Cognito, como custom:tenant_id. Si la API necesita el identificador del tenant en el access token, usa un mecanismo de personalización de tokens compatible con Cognito, como un Pre Token Generation Lambda trigger [SOURCE NEEDED].

La API debe derivar el contexto del tenant a partir del token validado, no de un tenant ID enviado por el navegador.

Separa la autenticación de la autorización

Cognito responde:

¿Quién es este usuario?

Tu aplicación todavía debe responder:

¿Qué puede hacer este usuario dentro de este tenant?

No trates la posesión de un token válido de Cognito como permiso para acceder a todos los recursos del tenant.

Un contexto de autorización útil podría contener:

{
  "sub": "550e8400-e29b-41d4-a716-446655440000",
  "tenant_id": "tenant_123",
  "roles": ["admin"]
}

sub es el identificador de usuario de Cognito. tenant_id establece el límite del tenant. roles representa los permisos de la aplicación.

Los grupos de Cognito pueden ser útiles para roles generales, pero crear un grupo para cada combinación de tenant y rol se vuelve difícil de gestionar a medida que crece el número de tenants. Para sistemas más grandes, la membresía del tenant y los roles pueden mantenerse en los datos de la aplicación y proyectarse en los claims de autorización cuando sea necesario.

Flujo de claim de tenant y solicitud de API

Un patrón práctico de implementación

1. Autentica con un único User Pool de Cognito

Para muchas aplicaciones SaaS, un único User Pool puede dar servicio a múltiples tenants. Cada usuario está asociado con un identificador de tenant.

Usar User Pools separados puede ofrecer una separación administrativa más fuerte, pero también agrega sobrecarga operativa.

2. Coloca el contexto del tenant en la identidad autenticada

Almacena o deriva un identificador de tenant para cada usuario.

No confíes en esta solicitud:

GET /bookings?tenantId=tenant_456

para decidir a qué tenant puede acceder quien llama.

En su lugar, extrae el identificador del tenant desde el JWT validado.

3. Valida el token en el límite de la API

API Gateway puede validar los JWTs emitidos por Cognito antes de invocar la lógica de la aplicación. El backend luego utiliza los claims validados que provee el authorizer.

4. Aplica el aislamiento de tenants en cada operación de datos

Supongamos que DynamoDB almacena citas.

Un diseño de clave útil es:

PK = TENANT#tenant_123
SK = APPOINTMENT#abc123

El backend construye la partition key a partir del claim de tenant autenticado.

Un handler de Lambda simplificado podría contener:

const claims = event.requestContext.authorizer.jwt.claims;
const tenantId = claims["tenant_id"];

if (!tenantId) {
  throw new Error("Missing tenant context");
}

const pk = `TENANT#${tenantId}`;

El cliente nunca decide la partition key.

5. Aplica las verificaciones de rol por separado

La membresía del tenant responde dónde puede operar el usuario. Los roles responden qué puede hacer el usuario.

Por ejemplo:

  • member puede ver citas.
  • manager puede modificar horarios.
  • admin puede gestionar personal.

Mantén estas verificaciones explícitas en la lógica de negocio o en una capa de autorización compartida.

Ejemplo: SaaS de reserva de citas

Considera una plataforma utilizada por negocios independientes.

María trabaja para tenant_123. Inicia sesión a través de Cognito y recibe un token que contiene su identidad, el contexto de tenant y su rol.

Ella solicita:

GET /appointments

API Gateway valida el token. La función Lambda lee tenant_123 desde los claims autenticados y consulta únicamente la partición de DynamoDB correspondiente a ese tenant.

Si María modifica la solicitud del navegador y envía tenant_456, el backend lo ignora. Si la API expone una ruta como /tenants/{tenantId}/appointments, el backend compara el tenant de la ruta con el tenant autenticado antes de acceder a los datos.

Esa verificación es el verdadero límite entre tenants.

Errores comunes de autenticación multi-tenant

Compensaciones y errores comunes

Confiar en los tenant IDs provistos por el cliente

Un tenant ID en un parámetro de solicitud es una entrada de datos, no una identidad. El contexto del tenant debe provenir de una fuente de autenticación validada.

Usar los grupos de Cognito como modelo completo de autorización

Los grupos funcionan bien para roles simples, pero los permisos de una aplicación SaaS pueden involucrar membresía específica por tenant, propiedad de recursos o acceso delegado. Esas reglas generalmente pertenecen a un modelo de autorización de la aplicación.

Asegurar la API pero no el modelo de datos

Diseña las claves de DynamoDB, los predicados SQL o los métodos del repositorio en torno al contexto del tenant, de modo que el aislamiento se aplique de forma consistente.

Colocar demasiada información en los tokens

Los claims de JWT son convenientes, pero los tokens permanecen válidos durante un período de tiempo. Por lo tanto, un cambio de rol puede no reflejarse hasta que se emita un nuevo token. El estado de autorización que cambia con frecuencia puede necesitar verificarse contra los datos de la aplicación en su lugar.

Conclusión

Cognito puede resolver la parte de identidad de la autenticación multi-tenant, pero el aislamiento de tenants sigue siendo una responsabilidad de la arquitectura de la aplicación. Trata el identificador del tenant como contexto de seguridad confiable, valídalo en el límite de la API, y llévalo a cada decisión de acceso a datos.

Como siguiente paso, traza una operación de API desde el token de Cognito hasta la consulta de base de datos, y verifica que ningún identificador de tenant controlado por el cliente pueda cambiar los recursos a los que accede.