Cloud Intelligence™
Deja de pelear con permisos complejos: un respiro para tu código
Esta página también está disponible en English, Deutsch, Français, Italiano, 日本語 y Português.
About Dima Kramskoy
Solutions architect who takes ideas from zero to proven. With over 20 years of software engineering, I build PoCs and MVPs on AWS that prove the path before you commit — spanning GenAI, DevOps, and FinOps. Off the clock: live-fire cooking, fishing, the outdoors, and applying Extreme Ownership to everything.
My personal pagePerfectScale™ for Kubernetes
Ready to optimize?
Get your free Kubernetes savings analysis

Resumen
Este artículo presenta Amazon Verified Permissions (AVP) como una solución práctica para gestionar la autorización compleja en aplicaciones, con un recorrido paso a paso usando el ejemplo PetStore de AWS.
El problema
¿Alguna vez te has topado con una complejidad enorme al intentar gestionar todos los roles, usuarios y actores de autorización de tu propio producto SaaS? ¿Te suena el dolor de los cambios en los requerimientos del negocio, tratando de aplicar los mejores principios de seguridad mientras haces malabares con code reviews y pruebas a/b relacionadas con los cambios que intentas implementar? Y sí, tu SaaS corre en la nube de AWS.
¡Creo que es hora de respirar hondo y ver el panorama completo! Piensa en tu solución, y vuélvela a pensar, ¡porque quizá pueda mostrarte una relativamente simple! ¡Una que te ayude a manejar la complejidad y a dedicar tus recursos y esfuerzos a lo que de verdad importa: tu negocio y sus necesidades!
Esa solución es Amazon Verified Permissions.
Cómo sueles manejar la autorización en tu aplicación web
Analicemos el sistema de autorización de una aplicación web. La necesidad de un sistema así surge con la evolución de la aplicación. Puedes planearlo con anticipación (lo cual es mejor, como parte del diseño de tu sistema), pero de todos modos enfrentarás cambios a medida que evolucionan los requerimientos del negocio. Tendrás que modificar distintas partes de tu sistema después de haberlo desplegado a producción.
¿Por qué necesitamos un sistema así? Por muchas razones:
- Quieres permitir que ciertos roles vean ciertas partes de tu aplicación
- Necesitas restringir ciertas acciones a roles o usuarios específicos de tu aplicación
- Necesitas reforzar los controles de seguridad y evitar accesos no autorizados
- Tu lógica de permisos termina regada por todo el código, y el mantenimiento se vuelve una pesadilla
Tu sistema de permisos típico: una maraña
La mayoría de los sistemas de autorización hoy se ven más o menos así: bloques de if/else dispersos por todo tu código:
// The typical nightmare in your Lambda or Node.js service:boolean userAuthorizedForOrder(Order order, User user) { if (user.storeId == order.storeId) { if (user.roles.contains("store-owner")) { return true; // store owners can access their own store's orders } else if (user.roles.contains("customer")) { if (user.id == order.customerId) { return true; // customers can only see their own orders } } } return false;}¿Y después? Agregas otro rol. Luego otro. Luego un caso especial para gerentes regionales. Luego una regla sobre quién puede cancelar pedidos después de 24 horas. Luego un nuevo requerimiento de negocio sobre el acceso los fines de semana. De repente te estás ahogando en condiciones anidadas, tus code reviews son brutales, cada cambio en los requerimientos del negocio se convierte en un despliegue, y la mitad de tu equipo discute casos límite en lugar de construir funcionalidades.
Los verdaderos puntos de dolor
Explosión de roles: Tus 2 roles se convierten en 5, luego en 10, y de pronto tienes un gerente-regional-con-permisos-de-empleado-de-tienda-pero-solo-los-fines-de-semana, y nadie puede llevar la cuenta de quién debería tener qué.
La pesadilla de los code reviews: Cada cambio de permisos requiere cambios de código. Los devs pasan horas debatiendo la lógica de las políticas en los PRs en lugar de lanzar funcionalidades.
Probar la autorización: ¿Cómo se prueban siquiera todas las combinaciones? Matriz de usuarios × roles × recursos × acciones = caos. Se te escapan casos límite y las cosas se rompen en producción.
Trazabilidad de auditoría: Buena suerte explicándole a compliance por qué tal usuario pudo acceder a tal pedido. Está enterrado en el historial de git, repartido en 5 archivos distintos.
Cero flexibilidad: ¿Quieres cambiar permisos un viernes? Mala suerte: eso es un despliegue. ¿Quieres revertir una política rápido? A desplegar otra vez.
Cómo puedes gestionarla con Amazon Verified Permissions
¿Y si tuvieras la oportunidad de simplificar esa carga? ¿Y si te dijera que ya existe un servicio que puede hacer por ti la mayor parte del trabajo de los permisos de tu aplicación web?
En lugar de incrustar la autorización en tu código, ¿qué tal si la separas por completo? ¿Qué tal si los permisos fueran declarativos, versionados, centralizados, y pudieras cambiarlos sin volver a desplegar tu aplicación?
Esa es la promesa de Amazon Verified Permissions (AVP).
La arquitectura
Así funciona (a grandes rasgos):
- El usuario se autentica vía AWS Cognito (o cualquier IdP)
- Tu API recibe la solicitud con un token JWT
- El Lambda Authorizer intercepta la solicitud
- La Lambda construye una consulta de autorización con: principal (quién), action (qué está haciendo), resource (sobre qué lo hace), context (atributos adicionales)
- La Lambda llama a AVP con esa consulta
- AVP evalúa las políticas de Cedar (un lenguaje de políticas diseñado para autorización)
- AVP devuelve ALLOW o DENY
- API Gateway aplica la decisión: la solicitud continúa o recibe un 403
La clave está aquí: Toda la lógica de permisos vive en políticas de Cedar dentro de AVP. Tu código solo pregunta "¿está autorizado?" y confía en la respuesta.

El ejemplo real: PetStore
Usé el repositorio PetStore de AWS como aplicación de ejemplo, incluyendo la mayoría de las funcionalidades descritas, pero con algunos ajustes, ya que el esquema incluido no funcionaba — probablemente por actualizaciones que AWS le hizo al servicio. Recuerda que es un servicio relativamente nuevo y asumo que en muchas áreas sigue en desarrollo.
Simplemente sigue los pasos indicados y empieza por desplegar la aplicación de Amplify. Es posible que te pida identificarte con tu cuenta de GitHub para clonar el repo y desplegarlo en tu ecosistema de AWS. Una vez desplegada, mirando el diagrama de arquitectura de arriba, podemos identificar los componentes y cómo se comunican entre sí.
La configuración: el paso a paso
Paso 1: usuarios y grupos en Cognito
Como proveedor de identidad usamos AWS Cognito. Después del despliegue, ve al dashboard de Amazon Cognito y deberías encontrar el User pool que empieza con "petstoresample"; ese es tu pool y ahí es donde debes crear los usuarios y grupos para esta aplicación.
Crea tres usuarios:
Necesitamos crear tres usuarios para esta PoC. Uno debe llamarse explícitamente "abhi", porque está hardcodeado dentro de la función Lambda. Los otros dos son más flexibles — yo los llamé "owner" y "visitor". Al crearlos:
- No envíes verificaciones por correo
- Márcalos como "email address verified"
- Usa contraseñas simples (no querrás restablecerlas después de la configuración)
Al terminar deberías tener algo así:

Agrega atributos personalizados al usuario owner:
No olvides actualizar al usuario "owner" con el atributo personalizado employmentStoreCode. Se usará más adelante como parte de las políticas en Amazon Verified Permissions. Solo haz clic en "Edit" y actualiza para que los atributos de tu usuario se vean así:

Crea los grupos y asigna los usuarios:
Necesitas crear 2 grupos y agregar los usuarios a esos grupos. Estos nombres de grupo ("Store-Owner-Role" y "Customer") forman parte de los tokens emitidos por Cognito y se usarán para el control de acceso basado en roles (RBAC).

El usuario "abhi" debe agregarse al grupo "Customer" junto con el otro usuario, "visitor":

Y obviamente el "owner", con la etiqueta adicional que agregamos, irá al grupo "Store-Owner-Role":

Paso 2: configuración de Amazon Verified Permissions
Con esta parte lista, lo siguiente es revisar y configurar Amazon Verified Permissions. En esta parte también vamos a revisar el Lambda Authorizer y el API Gateway — se desplegaron con el mismo despliegue de Amplify, como parte de la aplicación.
Simplemente busca Amazon Verified Permissions en el buscador y creemos primero un nuevo Policy store. En el repositorio recomiendan empezar por el esquema, pero la interfaz cambió: primero tienes que crear el Policy store. Puedes poner lo que quieras, ya que al final usaremos el esquema que te comparto. Así que puedes especificar un Empty Policy Store para simplificar las cosas; solo dale el nombre "PetStore".
Paso 3: define tu esquema
Esto es crucial. El esquema le dice a AVP qué principals, recursos, acciones y relaciones existen en tu aplicación. Usa este esquema:
{ "MyApplication": { "actions": { "GetOrder": { "appliesTo": { "principalTypes": ["User"], "resourceTypes": ["Order"] } }, "GetStoreInventory": { "appliesTo": { "principalTypes": ["User"], "resourceTypes": ["Application"] } }, "ListOrders": { "appliesTo": { "principalTypes": ["User"], "resourceTypes": ["Application"] } }, "PlaceOrder": { "appliesTo": { "principalTypes": ["User"], "resourceTypes": ["Application"] } }, "SearchPets": { "appliesTo": { "principalTypes": ["User"], "resourceTypes": ["Application"] } } }, "entityTypes": { "Application": { "memberOfTypes": [], "shape": { "attributes": { "storeId": {"type": "String"} }, "type": "Record" } }, "Order": { "memberOfTypes": [], "shape": { "attributes": { "owner": { "name": "User", "type": "Entity" }, "storeId": { "type": "String" } }, "type": "Record" } }, "Pet": { "memberOfTypes": [], "shape": { "attributes": { "owner": { "name": "User", "type": "Entity" }, "storeId": { "type": "String" } }, "type": "Record" } }, "Group": { "memberOfTypes": [], "shape": { "attributes": {}, "type": "Record" } }, "User": { "memberOfTypes": ["Group"], "shape": { "attributes": { "employmentStoreCode": {"type": "String"} }, "type": "Record" } } } }}El esquema del proyecto original tiene un pequeño problema, así que, en su lugar, copia este en el esquema de tu Policy Store.
Paso 4: crea las políticas de Cedar
Haz clic en el Policy Store y en el panel izquierdo verás el enlace "Policies". Haz clic ahí y luego en "Create Policy > Create static policy". El asistente de creación de políticas usa el esquema que agregamos. Estamos usando políticas permisivas — el alcance debe definirse para el grupo de participantes, el alcance del recurso debe ser el recurso específico donde eliges lo que necesitas, y el tipo de acción debe seleccionarse como un conjunto específico de acciones.
Hay un truco que puedes usar para ejecutar la política totalmente permisiva: selecciona "All principals", "All resources" y "All actions", guarda la política, luego edítala y simplemente pega la política en lenguaje Cedar. Es un atajo, pero necesitas conocer la declaración de tu política.
Estas son las políticas que necesitarías para esta PoC:
Ejemplo de RBAC — Los clientes pueden buscar mascotas y hacer pedidos:
permit( principal in MyApplication::Group::"Customer", action in [MyApplication::Action::"SearchPets", MyApplication::Action::"PlaceOrder"], resource == MyApplication::Application::"main");Ejemplo de ABAC — Los dueños de tienda solo pueden acceder al inventario de su propia tienda:
permit( principal in MyApplication::Group::"Store-Owner-Role", action == MyApplication::Action::"GetStoreInventory", resource == MyApplication::Application::"main")when { principal.employmentStoreCode == resource.storeId};Ejemplo de ABAC — Los usuarios solo pueden ver sus propios pedidos:
permit( principal in MyApplication::Group::"Customer", action == MyApplication::Action::"GetOrder", resource is MyApplication::Order)when { principal == resource.owner};Cloud bill shouldn't be a mystery
One platform for AI and Cloud optimization.
La integración con el Lambda Authorizer
Tu función Lambda recibe la solicitud de la API y necesita:
- Verificar el token JWT (extraer la información del usuario)
- Construir una consulta de autorización para AVP
- Llamar a la API
isAuthorizedde AVP - Devolver ALLOW/DENY
Así se ve (tomado directamente del repo de PetStore):
const AWS = require('aws-sdk');const avp = new AWS.Service({ apiConfig: require('./verifiedpermissions.json')});
const { CognitoJwtVerifier } = require("aws-jwt-verify");const jwtVerifier = CognitoJwtVerifier.create({ userPoolId: process.env.USERPOOLID, tokenUse: "id", clientId: process.env.APPCLIENTID});
const policyStoreId = process.env.POLICYSTOREID;
exports.handler = async (event) => { // Extract and verify the JWT token const token = event.headers["Authorization"]; let payload;
try { payload = await jwtVerifier.verify(token); var groups = payload["cognito:groups"] || [];
// Build the principal entity (the user) var userEntity = { "identifier": { "entityType": "MyApplication::User", "entityId": payload["cognito:username"] }, "attributes": { "employmentStoreCode": { "string": payload["custom:employmentStoreCode"] == null ? "" : payload["custom:employmentStoreCode"] } }, "parents": [] };
// Add groups as parent entities (for group membership) groups.forEach((group) => { userEntity.parents.push({ "entityType": "MyApplication::Group", "entityId": group }); });
// Build the authorization query let entities = { entityList: [userEntity] };
// Add resource entities based on the action addResourceEntities(entities, actionMap[event.httpMethod + event.resource], event.pathParameters);
// Call AVP let authQuery = { policyStoreId, principal: { "entityType": "MyApplication::User", "entityId": payload["cognito:username"] }, action: { "actionType": "MyApplication::Action", "actionId": actionMap[event.httpMethod + event.resource] }, resource: buildResource(actionMap[event.httpMethod + event.resource], event.pathParameters), entities };
const authResult = await avp.isAuthorized(authQuery).promise();
if (authResult.decision == 'ALLOW') { return buildResponse(200, 'Authorized', authResult); } else { return buildResponse(403, "You are not authorized to perform this action.", authResult); } } catch(err) { console.log(err); return buildResponse(403, "Error: " + err); }};Lo esencial: Tu código no contiene ninguna lógica de permisos. Solo le pasa el contexto a AVP y confía en la respuesta. De eso se trata todo.
El beneficio real: cambios sin redesplegar
Aquí es donde ocurre la magia.
Escenario: El negocio dice: "A partir del próximo mes, los usuarios de Store-Owner-Role también pueden cancelar pedidos, pero solo los realizados hace más de 24 horas".
Con los permisos tradicionales hardcodeados:
- Modificas el código de la Lambda
- Agregas una función
getOrderAge() - Escribes tests para la nueva lógica
- Haces commit, creas el PR, esperas el code review
- Alguien cuestiona tu lógica y tienes que iterar
- Haces merge, despliegas a staging, pruebas
- Despliegas a producción
- Si hay un bug, vuelves a desplegar mientras el negocio pierde dinero
Con Amazon Verified Permissions:
- Entras a la consola de AVP
- Agregas una nueva política de Cedar (toma 2 minutos):
permit( principal in MyApplication::Group::"Store-Owner-Role", action == MyApplication::Action::"CancelOrder", resource is MyApplication::Order)when { principal.employmentStoreCode == resource.storeId && resource.age > 1440 // minutes};- Guardas
- Ya está activa. De inmediato. Sin despliegue. Sin cambios de código. Sin redesplegar la Lambda.
Eso es todo. Tu código no cambió. Tu Lambda no se volvió a desplegar. La regla de autorización se actualizó en tiempo real.
RBAC + ABAC: lo mejor de ambos mundos
Cedar te permite combinar ambos enfoques:
Solo RBAC: "¿El usuario está en el grupo Admin?" (binario, no considera los atributos del recurso)
Solo ABAC: "¿El atributo department del usuario coincide con el atributo department del recurso?" (granular, pero requiere muchos atributos)
RBAC + ABAC (la fortaleza de Cedar): "¿El usuario está en el grupo Store-Owner-Role Y su employmentStoreCode coincide con el storeId del recurso?"
permit( principal in MyApplication::Group::"Store-Owner-Role", action == MyApplication::Action::"ManageInventory", resource is MyApplication::Application)when { principal.employmentStoreCode == resource.storeId};Esto te da lo mejor de ambos mundos: agrupación por roles + granularidad por atributos.
Pruebas y despliegue
Una vez definidas tus políticas, pruébalas:
- Como usuario
Customer: ¿Puede hacer SearchPets? ✅ Sí. ¿Puede hacer GetStoreInventory? ❌ No. - Como usuario
Store-Owner-Role: ¿Puede hacer GetStoreInventory de su tienda? ✅ Sí. ¿De otra tienda? ❌ No. - Como
ownerconemploymentStoreCode=petstore-london: ¿Puede acceder a los pedidos de petstore-london? ✅ Sí.
Si una política no funciona, revisa:
- ¿Tus entidades están bien estructuradas en la Lambda?
- ¿El JWT del usuario contiene los
cognito:groupscorrectos? - ¿El atributo personalizado está en el perfil del usuario?
- ¿Los tipos de entidad de tu consulta de autorización coinciden con tu esquema?
Conclusión: de la plomería al propósito
No planeaba escribir este artículo para presentar Amazon Verified Permissions como la bala de plata definitiva para tu aplicación, pero si estás en medio de un proceso interminable de ajustar el código de tu aplicación web basada en roles porque los requerimientos del negocio cambian con tanta frecuencia, tal vez valga la pena detenerte, tomar distancia y replantear lo que estás haciendo.
Quizás este servicio te ayude a ahorrar tiempo y recursos, y a liberar tu agenda para enfocarte en el negocio y no en la plomería. Al final, todo esto parece plomería: sí, sostiene los objetivos de negocio de tu aplicación, pero en realidad no ayuda a hacer crecer su valor.
Pero al menos con Amazon Verified Permissions, no estarás arreglando la plomería cada semana.