← Volver al blog
🪪 Arquitectura10 min de lectura·

¿Cómo administras usuarios de distintos proveedores de identidad?

Casi toda aplicación de negocio seria termina con el mismo problema: tiene dos tipos de usuarios con dos orígenes de identidad distintos. Los empleados internos ya tienen credenciales corporativas y quieren entrar con Single Sign-On. Los usuarios externos —clientes, proveedores, terceros— no tienen cuenta corporativa y necesitan credenciales propias. La pregunta no es «¿cómo hago login?», sino ¿cómo administro usuarios de proveedores de identidad distintos en un solo sistema, con las mismas sesiones, la misma API y el mismo modelo de roles. Este artículo muestra un patrón que resuelve exactamente eso usando Amazon Cognito como broker de identidad.

El problema: dos mundos de identidad, una sola app

Imagina una plataforma donde conviven dos poblaciones. Los internos son empleados de la organización: ya existen en un directorio corporativo (un proveedor de identidad empresarial que habla OIDC o SAML) y esperan entrar con sus credenciales de siempre, sin crear otra contraseña. Los externos son personas de fuera —contrapartes, clientes, auditores— que no están en ese directorio y para quienes crear cuentas en el IdP corporativo no tiene sentido ni es deseable.

Usuarios internos (empleados)InternoIdP Corp.ya tienen cuentaUsuarios externos (terceros)Externosin cuenta corporativanecesitan credenciales propias¿Un solo sistema de login?Mismas sesiones, misma API,mismos roles… dos orígenesde identidad distintos.
Dos poblaciones de usuarios con orígenes de identidad incompatibles, pero que deben compartir la misma aplicación.

Si intentas resolver esto «a mano», aparecen enseguida los dolores:

  • Dos sistemas de login paralelos: uno contra el IdP corporativo y otro con usuarios y contraseñas propios. Doble código, doble superficie de ataque, doble mantenimiento.
  • La API no sabe en quién confiar: cada origen emite tokens distintos, con formatos y firmantes distintos. El backend acaba lleno de condicionales según de dónde vino el usuario.
  • Los roles se mezclan con la identidad: si el rol de negocio viene en el token del IdP corporativo, dependes de que un equipo externo (el que administra el directorio) configure tu modelo de permisos. Y los externos ni siquiera están ahí.

La solución: Cognito como broker de identidad

La idea central es colocar Amazon Cognito en medio como broker (intermediario) de identidad. Un único User Pool hace de frontera de autenticación para toda la aplicación. Dentro de ese pool conviven las dos poblaciones:

  • Internos → identidades federadas. Cognito se configura con el IdP corporativo como proveedor OIDC federado. Cuando un interno hace login, Cognito lo delega al directorio corporativo (SSO), recibe el resultado y crea/actualiza un usuario federado en el pool.
  • Externos → usuarios nativos. Para los de fuera, la app crea usuarios directamente en Cognito con la API de administración (AdminCreateUser). Cognito les envía la invitación por correo y gestiona su contraseña, verificación y recuperación.
InternoExternoIdP Corp. (OIDC)Amazon CognitoUser Pool únicoBrokerInternos → federadosIdP OIDC externoExternos → nativosAdminCreateUserBFF / APIsolo habla con CognitoBackendSSOfederación OIDClogin nativo (Hosted UI)Auth Code + PKCEvalida JWT (JWKS)
Cognito actúa como broker: federa el IdP corporativo para los internos y aloja usuarios nativos para los externos. La aplicación solo habla con Cognito.

✅ La clave: la app solo conoce a Cognito

El backend y el BFF solo hablan con Cognito. El IdP corporativo queda «detrás», como un detalle de configuración del broker. Añadir mañana un segundo IdP (otro directorio, un proveedor social, otra federación SAML) es configurar un proveedor más en el mismo User Pool: la aplicación no cambia. Esa es toda la gracia del patrón de broker.

Cómo se federa el IdP corporativo (internos)

En Cognito, el directorio corporativo se registra como un Identity Provider de tipo OIDC dentro del User Pool. Cognito autodescubre los endpoints del IdP a partir de su documento de descubrimiento (/.well-known/openid-configuration) y se encarga del intercambio de tokens. Tú defines un mapeo de atributos: qué claim del IdP corporativo alimenta cada atributo de Cognito.

# Mapeo de atributos típico (IdP corporativo -> Cognito)
email          <- email
name           <- name
username       <- sub           # identificador estable del IdP
custom:origin  <- "interno"     # marca de dónde viene el usuario

En el login, el flujo real es un Authorization Code + PKCE encadenado: la app pide a Cognito iniciar sesión indicando el proveedor corporativo; Cognito redirige al directorio; el usuario se autentica con SSO; el directorio responde a Cognito; y Cognito emite sus propios tokens (ya normalizados) a la aplicación.

⚠️ Gotcha real: el secreto del IdP no entra en tu IaC

Al definir el proveedor OIDC por infraestructura como código (CloudFormation / CDK / Terraform), el client_secret del IdP corporativo normalmente no se puede inyectar en línea de forma segura (no se admite una referencia a secreto cifrado en ese campo). El patrón que funciona: crear el proveedor con un placeholder y setear el secreto real post-deploy con una llamada update-identity-provider, leyéndolo desde un gestor de secretos. Nunca en el repositorio.

Cómo se administran los externos (usuarios nativos)

Los externos no están en ningún directorio: los crea la propia aplicación. El backend usa la API administrativa de Cognito para darlos de alta bajo demanda, y Cognito se encarga del ciclo de vida de la credencial (invitación, contraseña temporal, cambio obligatorio en el primer acceso, recuperación).

# El backend crea el usuario externo; Cognito envía la invitación por email
cognito.admin_create_user(
    UserPoolId=POOL_ID,
    Username=email,
    UserAttributes=[
        {"Name": "email", "Value": email},
        {"Name": "email_verified", "Value": "true"},
        {"Name": "custom:origin", "Value": "externo"},
    ],
    DesiredDeliveryMediums=["EMAIL"],
)

Como ambos tipos de usuario viven en el mismo User Pool, la API valida tokens de una sola forma: verifica la firma del JWT contra el JWKS del pool (RS256) y comprueba el issuer. No importa si por detrás el usuario vino del directorio corporativo o es nativo: para el backend, todos son «usuarios de Cognito».

La decisión que lo cambia todo: identidad ≠ autorización

Aquí está el punto más importante del diseño, y el que más gente se salta: Cognito (y el IdP corporativo) autentican, pero no autorizan. El token dice quién es el usuario (su sub, su email, su origen), no qué puede hacer en tu negocio. Los roles de negocio viven en la base de datos de la aplicación, no en el token.

Autenticación — ¿quién eres?Cognitoentrega identidad verificada(sub, email, origen)sin roles de negocio en el tokenAutorización — ¿qué puedes hacer?BD apptabla de mapeo de rolesorigen externo → rol internose cambia sin re-desplegarSesión server-sideDynamoDBtokens OAuth del ladoservidor, con TTLel navegador solotiene una cookie HttpOnlyidentidadrol resuelto
Cognito entrega identidad verificada; la base de datos de la aplicación resuelve el rol. La sesión vive server-side en DynamoDB.

¿Por qué separar identidad de autorización?

  • No dependes de un equipo externo para tus permisos. Si el rol viniera como claim del directorio corporativo, cada cambio de permisos sería un ticket al equipo que administra ese directorio. Con el rol en tu BD, lo gestionas tú.
  • Los externos encajan sin excepción. No están en el directorio corporativo, así que jamás tendrían un claim de rol. Si la autorización vive en tu BD, internos y externos se tratan igual.
  • Cambias permisos sin re-desplegar. Una tabla de mapeo (origen/grupo externo → rol interno) se actualiza con una fila, no con un despliegue.
-- Tabla de mapeo de roles en la BD de la app (no en el token)
-- Cambiar el rol efectivo de un grupo externo es un UPDATE, sin re-deploy:
UPDATE app_role_mapping
   SET internal_role = 'approver'
 WHERE external_role = 'analista';

💡 Los grupos del IdP llegan como identificadores opacos

Un detalle práctico: muchos directorios corporativos no envían el nombre legible del grupo, sino un identificador opaco (GUID). Si piensas usar esos grupos, tu tabla de mapeo debe traducir ese identificador a un rol de tu dominio. Otra razón más para que la autorización sea responsabilidad de tu aplicación, no del token.

Dónde viven los tokens: sesión server-side en DynamoDB

El último pilar es no exponer los tokens al navegador. En lugar de que la SPA guarde el access_token, un BFF (Backend For Frontend)hace el flujo OAuth, guarda los tokens del lado servidor y entrega al navegador solo una cookie de sesión HttpOnly, Secure y SameSite. El almacén de esas sesiones es Amazon DynamoDB, con TTL para que las sesiones expiren solas.

  • DynamoDB con TTL es ideal para sesiones: clave-valor, baja latencia, serverless y con expiración automática por registro. Cero servidores de sesión que mantener.
  • El navegador nunca ve el JWT. Solo tiene una cookie que su propio JavaScript no puede leer (HttpOnly), lo que elimina el robo de token por XSS.
  • Anti-CSRF con double-submit token en las operaciones que mutan estado, complementando el SameSite de la cookie.

El flujo completo, de principio a fin

Juntando todas las piezas, un login de usuario interno se ve así:

  • El usuario pulsa «Ingresar con SSO». El BFF inicia Authorization Code + PKCE contra Cognito, indicando el proveedor corporativo.
  • Cognito redirige al directorio corporativo. El usuario se autentica con sus credenciales de empresa.
  • El directorio responde a Cognito; Cognito federa/actualiza al usuario y emite el código de autorización.
  • El BFF canjea el código por tokens, los guarda en DynamoDB y entrega al navegador una cookie de sesión HttpOnly.
  • El backend resuelve el usuario por su sub/email, consulta su rol en la BD y aplica autorización.
NavegadorSPABFFservidorCognitobrokerIdP Corp.directorioDynamoDBsesiónBackend+ BD1«Ingresar con SSO»Authorization Code + PKCE (idp=corporativo)2redirige a login corporativousuario se autentica (SSO)3respuesta OIDC (identidad + grupos)federa/actualiza usuario → code4canjea code por tokensguarda tokens (TTL)cookie de sesión HttpOnly5request con cookie → resuelve sub/email + rol (BD)el token nunca llega aquí
Login de un usuario interno de principio a fin. Los números coinciden con los pasos de arriba. Observa que el token nunca llega al navegador: solo recibe una cookie de sesión.

Para un usuario externo, cambia solo el principio: en vez de delegar en el directorio corporativo, Cognito valida la contraseña nativa (gestionada por él). Del canje de tokens en adelante, el flujo es idéntico — y por eso el backend no necesita saber de qué mundo viene cada quien.

Mirada Well-Architected

Este patrón no es solo «que funcione el login»: se alinea con varios pilares del AWS Well-Architected Framework de forma natural.

Seguridad

  • Identidad gestionada por un servicio dedicado (Cognito), no reinventada en la app.
  • Tokens fuera del navegador (patrón BFF + cookie HttpOnly): se elimina el robo por XSS.
  • Validación de JWT por firma (JWKS/RS256) e issuer; autorización con least privilege resuelta en la app.
  • Secretos (del IdP y del app client) en un gestor de secretos, nunca en el código.

Excelencia operativa

  • Un solo flujo de validación de tokens: menos ramas, menos bugs, más fácil de operar.
  • Añadir o quitar proveedores de identidad es configuración, no reescritura.
  • Roles gestionados en datos (una tabla), auditable y modificable sin despliegues.

Fiabilidad y costo

  • Sesiones en DynamoDB con TTL: almacén serverless, sin instancias que escalar ni limpiar.
  • Cognito y DynamoDB escalan de forma gestionada; pagas por uso, sin capacidad ociosa.

⚠️ Defensa en profundidad: el broker no es el final

El broker de identidad resuelve de dónde vienen los usuarios y cómo los autenticas de forma unificada. Pero sigue siendo necesario endurecer el resto: rate limiting en los endpoints públicos (p. ej. AWS WAF), CSP estricta, validación de entradas, rotación de secretos y observabilidad de los eventos de autenticación.

En resumen

Cuando tienes usuarios de proveedores de identidad distintos, la respuesta no es construir dos sistemas de login: es poner un broker de identidad en medio. Amazon Cognito federa el directorio corporativo para los internos (SSO) y aloja usuarios nativos para los externos, todo en un único User Pool. La aplicación habla con un solo proveedor, valida los tokens de una sola forma y mantiene la autorización en su propia base de datos, desacoplada de la identidad. Si a eso le sumas un BFF con sesiones en DynamoDB y cookies HttpOnly, obtienes un sistema de identidad unificado, seguro y alineado con Well-Architected — que mañana admite un proveedor más con solo configurarlo.