authentik en AWS: tu propio IdP con CloudFormation, IAM Identity Center y ALB
Tu propio proveedor de identidad en AWS, paso a paso y con los errores reales: authentik desplegado con CloudFormation, login a la consola de AWS y una app protegida por el ALB sin escribir código de login.
En este post vas a desplegar authentik, un proveedor de identidad open source, en tu propia cuenta de AWS, y lo vas a usar para entrar a la consola de AWS y para proteger una app que no tiene ni una línea de código de login. Todo con servicios administrados: ECS Fargate, RDS, un Application Load Balancer e IAM Identity Center.
Es el material de apoyo de mi charla en el AWS Community Day Paraguay 2026, pero está escrito para que puedas seguir sin haberla visto. Incluye los templates, los comandos y, sobre todo, los errores que me encontré armando la demo, que son los que no aparecen en los tutoriales.
Antes de arrancar, una aclaración: si solo querés probar authentik o correrlo para un equipo chico, Docker Compose es probablemente el camino más sencillo, y también el más barato: en una t4g.medium (2 vCPU, 4 GB) sale unos US$ 30 por mes en us-east-1, contando disco e IP pública. A cambio, los backups, el TLS y la redundancia quedan a tu cargo. Si ya tenés un cluster de Kubernetes, el Helm chart también es una buena opción.
El template oficial de CloudFormation está armado con otra escala en mente: servicios administrados, redes privadas y redundancia, con un costo que ronda los US$ 400 por mes. Para mí apunta a organizaciones medianas o grandes que ya viven en AWS. En este post uso una versión recortada de ese template, más barata, para mostrar cómo encaja authentik con IAM Identity Center y el ALB, y al final explico qué volver a agregar si lo querés llevar a producción.
Al final vas a tener:
- authentik corriendo en
https://auth.tudominio.com, desplegado con CloudFormation. - SSO a la consola de AWS con IAM Identity Center y SAML.
- Usuarios y grupos que se crean y se borran solos en AWS cuando los cambiás en authentik, con SCIM.
- Una app interna detrás del ALB que solo deja pasar a quien se logueó en authentik, con OIDC.
Asumo que sabés moverte en la consola de AWS y en una terminal. No hace falta experiencia previa con CloudFormation ni con authentik.
Qué vamos a armar

El ALB recibe todo el tráfico y lo separa por host. Lo que va a auth llega a authentik, que guarda su estado en RDS y EFS. Lo que va a app pasa primero por la autenticación OIDC del ALB contra authentik. Por otro lado, authentik le manda a IAM Identity Center los usuarios y grupos por SCIM y le resuelve el login por SAML, y con eso se entra a la consola de AWS.
Antes de empezar
Necesitás una cuenta de AWS con permisos de administrador, la AWS CLI configurada y un dominio en el que puedas crear registros DNS. En los ejemplos uso auth.tudominio.com para authentik y app.tudominio.com para la app protegida; en las capturas vas a ver los míos, demo.melizeche.com y whoami.melizeche.com. Todo va en us-east-1: el certificado tiene que estar en la misma región que el stack.
El certificado de ACM
El ALB necesita un certificado de AWS Certificate Manager. Pedilo en ACM como certificado público, con los dos nombres (auth y app) y validación por DNS. ACM te da un registro CNAME por nombre: crealo en tu DNS y esperá a que el certificado pase a Issued. Si tu DNS está en Cloudflare, dejá esos registros en DNS only.

Ojo con esta captura: los dos dominios dicen Success y el certificado sigue en Pending validation. En mi caso terminó en FAILED con motivo CAA_ERROR. Mi dominio tenía registros CAA que autorizaban solo a otras autoridades certificantes, y Amazon no estaba entre ellas. Lo arreglé agregando un registro CAA 0 issue "amazon.com" y pidiendo un certificado nuevo, porque uno en FAILED no se puede reintentar. Si lo pasás por alto, el error aparece recién al crear el stack, en el listener HTTPS del ALB, y con un mensaje que no ayuda: "must have a fully-qualified domain name, a supported signature, and a supported key size".
Para ver el motivo real de un certificado fallido:
aws acm describe-certificate --region us-east-1 --certificate-arn <arn> \
--query "Certificate.{Status:Status,Reason:FailureReason}"Cuánto cuesta
Con el template oficial y sus valores por defecto, el stack cuesta alrededor de US$ 0,55 por hora (unos US$ 400 por mes) en us-east-1. La mayor parte es RDS Multi-AZ. Para la demo armé una variante mínima que baja a unos US$ 0,13 por hora, o sea unos US$ 6 si lo dejás prendido 48 horas. En la sección de producción está el detalle y una configuración intermedia. Son precios on-demand, sin tráfico ni impuestos; revisalos en la página de precios de cada servicio antes de decidir.
Paso 1: desplegar authentik con CloudFormation
authentik publica un template oficial de CloudFormation, documentado en la guía de instalación en AWS. Crea una VPC, una base RDS PostgreSQL, un cluster ECS con una tarea para el server y otra para el worker, un ALB con tu certificado, dos volúmenes EFS y los secretos en Secrets Manager. Desde la versión 2025.10 authentik ya no usa Redis, así que no hay ElastiCache.
Para la demo usé una variante mínima del template oficial. Estos son los cambios:
| Qué | Oficial | Variante demo |
|---|---|---|
| Base de datos | db.m5.large, Multi-AZ | db.t4g.small, Single-AZ, sin backups |
| Tareas server / worker | 2 / 2 | 1 / 1 |
| NAT Gateway | 1 | ninguno: las tareas van en subredes públicas, con el security group aceptando solo al ALB |
| Al borrar el stack | RDS deja snapshot, EFS y logs se conservan | se borra todo |
Los archivos están en https://github.com/melizeche/authentik_aws_demo. Para crear el stack desde la consola: CloudFormation, Create stack, Upload a template file.

En los parámetros, lo obligatorio es el ARN del certificado. Fijá también la versión de authentik: usá 2026.8.4 o una posterior. Desde esa versión el template oficial se conecta a PostgreSQL con TLS y deja que RDS elija la versión menor de Postgres (PR #26807). Si usás un template anterior, fijá una versión de Postgres que RDS ofrezca hoy (yo usé 17.11), porque el default viejo es 17.1 y RDS va retirando versiones menores. Las versiones disponibles las podés listar con aws rds describe-db-engine-versions --engine postgres.

Al final tenés que tildar que CloudFormation puede crear roles de IAM. Desde la CLI es lo mismo con aws cloudformation deploy ... --capabilities CAPABILITY_IAM. Mientras se crea, la pestaña Events muestra cada recurso; si algo falla, el primer CREATE_FAILED tiene el motivo real.

Lo que me trabó en este paso:
- gp3 con 10 GB. Para ahorrar cambié el disco de la base a gp3 y RDS lo rechazó: para PostgreSQL, gp3 exige al menos 20 GB. Volví a gp2, que es lo que usa el template oficial.
no pg_hba.conf entry ... no encryption. RDS con PostgreSQL 15 o más rechaza conexiones sin TLS, y authentik por defecto se conecta consslmode: disable, así que los contenedores quedan reintentando para siempre. Está corregido en el template oficial desde authentik 2026.8.4 (PR #26807). Si usás un template anterior, la solución es agregarAUTHENTIK_POSTGRESQL__SSLMODE=requireal server y al worker.- El stack queda en
CREATE_IN_PROGRESShoras. El template no activa el circuit breaker de ECS, entonces si el contenedor se cae en loop CloudFormation espera. Para destrabarlo, bajá a 0 las tareas de los dos servicios en ECS, esperá a que el stack termine y después aplicá el arreglo con un update.
Cuando el stack termine, copiá el LoadBalancerDNS de la pestaña Outputs, creá un CNAME auth que apunte ahí y entrá a https://auth.tudominio.com/if/flow/initial-setup/ para crear el usuario akadmin. Hacelo enseguida: hasta ese momento, cualquiera que encuentre la URL puede quedarse con la instancia.
Paso 2: SSO a la consola de AWS con IAM Identity Center
La idea es que nadie tenga usuarios IAM propios: entrás a authentik y desde ahí llegás a la consola de AWS con los permisos de tu grupo. Seguí la guía oficial de la integración; acá van los pasos con lo que no dice.
Antes de empezar. IAM Identity Center tiene que estar habilitado como instancia de organización, en la cuenta de management de AWS Organizations; una instancia de cuenta no da acceso a las cuentas de AWS. Si tu cuenta no está en una organización, creála con aws organizations create-organization --feature-set ALL y habilitá Identity Center. Y tené en cuenta que cambiar el identity source afecta a todos los que hoy entran por el access portal: desde ese momento el login pasa por authentik.

1. Bajá la metadata de AWS. En Identity Center, Settings, Identity source, Actions, Change identity source, External identity provider. Bajá la metadata del service provider y dejá la pestaña abierta.

2. Creá la aplicación en authentik. Applications, New Application, y como tipo de provider SAML Provider from Metadata, subiendo el archivo de AWS.


En el provider, el NameID tiene que ser authentik default SAML Mapping: Email: AWS cruza el NameID con el username de Identity Center, y vamos a hacer que ese username sea el email.

3. Subí la metadata de authentik a AWS. En authentik, en el provider, Related objects, Metadata, Download. En la pestaña de AWS, subí ese archivo en IdP SAML metadata (no hace falta cargar el certificado aparte, ya viene adentro), escribí ACCEPT y confirmá.

4. Asigná permisos. En AWS accounts, tildá la cuenta, Assign users or groups, el grupo que va a llegar por SCIM (aws-admins) y un permission set. Para una demo, ReadOnlyAccess alcanza.



El error que me encontré: al probar el login, authentik respondió Bad Request: ACS URL of https://us-east-1.sso.signin.aws/... doesn't match Provider ACS URL of https://us-east-1.signin.aws/.... Al importar la metadata, el provider se quedó con una URL de ACS distinta de la que AWS usa en el request. Fijate que en la captura del paso 1 AWS muestra la que tiene sso.. La solución es editar el provider y pegar en ACS URL exactamente la que muestra AWS. El Audience no se toca: es el issuer de Identity Center y no tiene por qué compartir el host con el ACS.
Paso 3: aprovisionamiento automático con SCIM
Con SAML solo, IAM Identity Center no te deja entrar si el usuario no existe ya en su directorio. SCIM resuelve eso: authentik crea, actualiza y borra usuarios y grupos en AWS cada vez que cambian. Es lo que hace que dar de baja a alguien en authentik le corte el acceso a AWS. Como el permission set del paso anterior se asigna a un grupo que llega por SCIM, en la práctica ese paso va después de este.
1. Activá el aprovisionamiento en AWS. En Settings, Automatic provisioning, Enable. Copiá el endpoint de SCIM y el token: el token se muestra una sola vez.

2. Creá un property mapping. En authentik, Customization, Property Mappings, SCIM Provider Mapping. El nombre tiene que ordenarse después del mapping por defecto, por eso el zz. Saca el atributo photos y usa el email como userName, para que coincida con el NameID de SAML:
return {
"photos": None,
"userName": request.user.email,
}
3. Creá el provider SCIM con la URL y el token de AWS, y Compatibility Mode en AWS.

En Group Filter elegí solo el grupo que querés mandar a AWS y dejá Exclude service accounts activado. Sin filtro, authentik intenta sincronizar a todos los usuarios, akadmin incluido.

4. Asociá el SCIM a la aplicación. En la aplicación de AWS, Backchannel providers, agregá el provider SCIM. Sin esto la tarea de sincronización termina con el warning "No provider found. Is it assigned to an application?".

5. Probá con dry run. El provider tiene un modo Dry run que no manda nada a AWS y deja en los logs de la tarea el request que habría hecho. Vale la pena mirarlo antes de activar el sync de verdad, porque ahí vi el siguiente problema.

El usuario admin1 no tenía nombre en authentik, y el body salía con givenName: "" y displayName: "". IAM Identity Center exige nombre, apellido y display name, así que lo rechazó. Peor: en authentik el error aparecía como "Network error communicating with remote system", que te manda a buscar un problema de red que no existe. Mirando el código, el mensaje real de AWS queda escondido cuando la respuesta es un 4xx; el detalle está en los logs del worker, en la línea Failed to send SCIM request. La solución es simple: que todo usuario que vaya a AWS tenga nombre y apellido.

Con eso, desactivá el dry run, lanzá el sync y fijate que el usuario y el grupo aparezcan en Users y Groups de Identity Center. Después asigná el permission set (paso 4 de la sección anterior) y probá entrar desde el dashboard de authentik: tenés que llegar a la consola sin otro login.

Paso 4: un ALB con OIDC delante de una app sin código de login
El Application Load Balancer puede autenticar usuarios antes de mandarle el request a la app, con una acción authenticate-oidc en la regla del listener. La app no implementa ningún login: el ALB redirige a authentik, canjea el código por los tokens y le pasa a la app la identidad del usuario en headers. Para cualquier herramienta interna que hoy está protegida con una contraseña compartida, esto es oro.
La configuración está en la guía oficial de la integración con AWS ALB. Acá van los pasos tal como los hice, con los errores que me encontré y que la guía no cubre.
1. Creá la aplicación en authentik con un provider OAuth2/OpenID:
- Client type: Confidential.
- Redirect URI de tipo Authorization, en modo Strict:
https://app.tudominio.com/oauth2/idpresponse. Esa ruta la maneja el ALB, no tu app. En versiones anteriores a 2026.5 no hay tipo: cargá solo esa URI. - Signing key: el certificado autofirmado de authentik, para que el ID token salga firmado con RS256.
- En Advanced protocol settings, desmarcá Include claims in id_token. El ALB lee los claims del userinfo, y si quedan también en el token suman al límite de tamaño del ALB.
- En Bindings, el grupo que puede entrar.
Copiá el client ID y el client secret.
2. Desplegá la app y la regla del ALB. Armé un template aparte que reusa el cluster, la VPC y el ALB del stack de authentik y crea el servicio ECS de la app, su target group y la regla del listener 443 con dos acciones: primero authenticate-oidc y después forward. La parte importante de la regla:
Actions:
- Type: authenticate-oidc
Order: 1
AuthenticateOidcConfig:
Issuer: https://auth.tudominio.com/application/o/<slug>/
AuthorizationEndpoint: https://auth.tudominio.com/application/o/authorize/
TokenEndpoint: https://auth.tudominio.com/application/o/token/
UserInfoEndpoint: https://auth.tudominio.com/application/o/userinfo/
ClientId: <client id>
ClientSecret: <client secret>
Scope: openid email profile
OnUnauthenticatedRequest: authenticate
- Type: forward
Order: 2
TargetGroupArn: <target group de la app>Después creá el CNAME app apuntando al mismo DNS del ALB. Los dos nombres van al mismo balanceador; la regla separa el tráfico por host.
3. Lo que tuve que arreglar, en el orden en que apareció:
- El target nunca quedaba healthy (
Target.Timeout). El security group del ALB del template de authentik solo permite salida al puerto 9000 de las tareas de authentik. Hay que agregarle salida al puerto 80 del security group de la app. Mientras tanto, el circuit breaker de ECS hacía fallar el stack y el rollback borraba la evidencia. - Redirect URI Error en authentik: el redirect URI cargado en el provider no coincidía exactamente con el que manda el ALB. Revisá el
redirect_urien la URL de esa pantalla y que elclient_idsea el del provider que estás editando.

- 500 en
/oauth2/idpresponse. Después del login, es el ALB el que llama al token endpoint de authentik, no el navegador. Acá eso significa llamarse a sí mismo por su IP pública, y el security group no permitía salida al 443. Agregar esa regla lo resolvió. - 561 Authentication Error. En los logs del server de authentik, el
POST /application/o/token/del user agentAWSELB/2.0volvía con 400, y un warning justo antes decíaInvalid client secret. El secret de la regla no era el del provider. Se corrige desde la consola de EC2 editando la regla, sin redesplegar.
4. Qué recibe la app. Con todo andando, la app recibe estos headers del ALB:
X-Amzn-Oidc-Data: un JWT firmado por el ALB con ES256, con los claims del userinfo de authentik (nombre, email, grupos).X-Amzn-Oidc-Identity: elsubdel usuario, en texto plano y sin firma.X-Amzn-Oidc-Accesstoken: el access token que emitió authentik, también sin firma del ALB. Ni este ni el anterior sirven para identificar al usuario.

Una app real tiene que verificar la firma de X-Amzn-Oidc-Data con la clave pública del ALB para la región (https://public-keys.auth.elb.<región>.amazonaws.com/<kid>) y después chequear que el campo signer del header sea el ARN de tu ALB, que exp no haya pasado, que iss sea el issuer de authentik y que client sea tu client ID. Ojo con un detalle: el ALB codifica el JWT en base64url con padding =, y la firma cubre el header y el payload tal como llegan, padding incluido. Muchas librerías de JWT rechazan ese padding, pero si se lo sacás antes de verificar cambiás los bytes firmados, y la firma falla cada vez que el payload tiene padding. Me pasó: la app anduvo un tiempo y después empezó a responder “Signature verification failed”. Hay que verificar la firma sobre los segmentos originales y quitar el padding solo para decodificarlos. Y todo esto solo es seguro si la app acepta tráfico únicamente desde el ALB: si alguien llega directo al contenedor, puede inventarse los headers. Para la demo escribí una app chica en Python que hace exactamente eso y muestra quién está logueado; está en el repo.

Los errores, en una tabla
Si llegaste acá buscando un mensaje de error, esta es la versión corta de todo lo anterior.
| Síntoma | Causa | Arreglo |
|---|---|---|
Certificado de ACM en FAILED, motivo CAA_ERROR | El dominio tiene registros CAA que no autorizan a Amazon | Agregar 0 issue "amazon.com" y pedir un certificado nuevo |
| Listener HTTPS falla: "must have a fully-qualified domain name, a supported signature..." | El certificado no está emitido | Esperar a Issued o revisar el motivo del fallo |
| RDS: "Invalid storage size for engine name postgres and storage type gp3: 10" | gp3 exige al menos 20 GB en PostgreSQL | Usar gp2 o subir a 20 GB |
no pg_hba.conf entry ... no encryption | RDS con PostgreSQL 15+ exige TLS y el template oficial anterior a 2026.8.4 no lo configura | AUTHENTIK_POSTGRESQL__SSLMODE=require en server y worker (corregido en 2026.8.4, #26807) |
Stack en CREATE_IN_PROGRESS por horas | Tareas ECS en loop y sin circuit breaker | Bajar las tareas a 0, dejar terminar el stack y corregir con un update |
| Bad Request: ACS URL ... doesn't match Provider ACS URL | El provider SAML guardó otra URL de ACS al importar la metadata | Pegar en el provider la ACS URL que muestra AWS |
| SCIM: "Network error communicating with remote system" | En realidad es un 4xx de AWS, por ejemplo un usuario sin nombre | Ver Failed to send SCIM request en los logs del worker y completar el nombre |
| SCIM: "No provider found. Is it assigned to an application?" | El provider SCIM no está como backchannel de la aplicación | Agregarlo en Backchannel providers |
ECS: "ECS Deployment Circuit Breaker was triggered" y target Target.Timeout | El security group del ALB no tiene salida hacia la app | Egress del ALB al puerto de la app |
| authentik: Redirect URI Error | El redirect URI del provider no coincide con el que manda el ALB | https://<host>/oauth2/idpresponse en modo Strict |
500 en /oauth2/idpresponse | El ALB no puede llegar al token endpoint | Egress del ALB al 443 |
500 en /oauth2/idpresponse con usuarios de muchos grupos | Los claims más el access token superan 11 KB | Un scope mapping profile propio que devuelva menos grupos |
| 561 Authentication Error | authentik rechazó el canje del código; en mi caso, Invalid client secret | Corregir el secret en la regla del listener |
| La app: Signature verification failed después de andar bien | Se le sacó el padding = al JWT antes de verificar, y la firma lo incluye | Verificar la firma sobre los segmentos tal como llegan |
De demo a producción
La variante demo está pensada para prender, mostrar y borrar. Un proveedor de identidad en producción es otra cosa: si se cae, nadie entra a nada. Esta es la configuración mínima que yo usaría para usuarios reales. Es una propuesta, no un template probado.
| Template oficial | Variante demo | Mínimo para producción | |
|---|---|---|---|
| Base de datos | db.m5.large Multi-AZ | db.t4g.small Single-AZ | db.t4g.small Multi-AZ |
| Backups de RDS | retención por defecto | desactivados | 7 días, con point-in-time recovery |
| Deletion protection | no | no | sí |
| TLS a Postgres | require desde 2026.8.4 | require | verify-full con el CA de RDS |
| Tareas server / worker | 2 / 2 | 1 / 1 | 2 / 1 |
| Red de las tareas | privada, con NAT | pública, sin NAT | privada, con NAT |
| Circuit breaker de ECS | no | no | sí, con rollback |
| Backups de EFS | no | no | sí |
| Logs | sin vencimiento | 3 días | 30 días |
| Costo aproximado (us-east-1) | ~US$ 400/mes | ~US$ 97/mes | ~US$ 172/mes |
Lo que no entra en la tabla:
AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS. Desde la 2026.8, authentik solo confía en los headersX-Forwarded-*si vienen de una red de proxies confiables. Detrás del ALB conviene setearla con el CIDR de la VPC para tener las IPs reales en los logs y las políticas.- Un
SessionCookieNamepor regla. Si varias apps comparten el ALB, cada regla conauthenticate-oidcnecesita su propio nombre de cookie de sesión. Y revisá elSessionTimeout: por defecto son 7 días; en la demo usé una hora. - La configuración de authentik también es infraestructura. Exportá las aplicaciones, providers y mappings como blueprints, o manejalos con el provider de Terraform
goauthentik/authentik. Si tenés que recrear el stack, la recuperás en minutos.
Los costos salen de las tarifas on-demand de cada servicio en us-east-1, sin tráfico, NAT por GB, logs ni impuestos.
Limpieza y recursos
Cuando termines, borrá primero el stack de la app y después el de authentik: la regla de la app cuelga del listener del ALB y traba el borrado si va al revés. Si agregaste reglas de security group a mano mientras depurabas, sacalas antes. En la variante demo nada tiene Retain ni deja snapshot, así que con eso la factura vuelve a cero; con el template oficial, revisá que no queden EFS, log groups ni el snapshot de RDS.
Si cambiaste el identity source de IAM Identity Center a authentik, volvelo al directorio de Identity Center antes de borrar el stack. Si no, nadie va a poder entrar por el access portal.
Recursos
- Instalación de authentik en AWS
- Integración con IAM Identity Center (SAML y SCIM)
- Integración con AWS ALB (OIDC)
- Autenticación de usuarios con un Application Load Balancer
- Consideraciones al cambiar el identity source
- Templates, scripts y la app de ejemplo: https://github.com/melizeche/authentik_aws_demo