Introducción
La API de pedidos debe exigir inicio de sesión para lecturas y escrituras mientras mantiene pública la salud. Adjuntarás comprobaciones de tokens a ambas rutas de pedidos, probarás solicitudes aceptadas y rechazadas y confirmarás que las llamadas rechazadas dejan los datos de negocio sin cambios.
Completa primero Añadir inicio de sesión de la aplicación con Cognito y Construir y validar una API de pedidos. Este entorno nuevo proporciona un backend público funcional y componentes sintéticos de identidad; no se reutilizan recursos ni tokens anteriores.
Relación con las certificaciones
Este laboratorio ofrece práctica para los siguientes temas de examen.
- Solutions Architect – Associate (SAA-C03) · Tarea 1.2: Autorización API basada en tokens y pruebas de límites de acceso.
- Developer – Associate (DVA-C02) · Tarea 2.1: Autorización API basada en tokens y pruebas de límites de acceso.
- Security – Specialty (SCS-C03) · Tarea 4.1: Práctica de fundamentos: Autorización API basada en tokens y pruebas de límites de acceso.
Inspeccionar las rutas públicas e iniciar sesión
En este paso, establecerás el estado actual de las rutas públicas y obtendrás un token de acceso real para el cliente de aplicación previsto.
Entra en el espacio de trabajo y mantén privados los archivos de tokens recién creados:
cd /home/labex/project
umask 077
Abre AWS View, junto a Terminal, para comparar solicitudes de API, ejecuciones de función y pedidos almacenados. Los registros de solicitudes de la puerta de enlace y los registros de ejecución de Lambda son separados: un token rechazado debe detenerse antes de que se ejecute la función.
Lee el inventario seguro de componentes de prueba. Identifica el grupo y cliente previstos, otro cliente, otro emisor y el grupo de referencia ajeno al laboratorio:
cat scenario.json
Usa jq -r para seleccionar los ID principales y almacena la salida mediante la sustitución de comandos $(...):
POOL_ID=$(jq -r '.main.pool' scenario.json)
CLIENT_ID=$(jq -r '.main.client' scenario.json)
Encuentra la API HTTP preparada por su nombre y registra su ID generado para la limpieza:
API_ID=$(aws apigatewayv2 get-apis \
--query "Items[?Name=='labex-a05'].ApiId | [0]" \
--output text)
printf '%s\n' "$API_ID" > api-id.txt
Inspecciona los tipos de autorización de las rutas:
aws apigatewayv2 get-routes \
--api-id "$API_ID" \
--query 'Items[].{Route:RouteKey,Authorization:AuthorizationType}'
Las tres rutas usan inicialmente NONE. El directorio de usuarios por sí solo no ha protegido la API. Construye su dirección del espacio de trabajo:
API_URL="http://127.0.0.1:8081/api/$API_ID"
curl -i "$API_URL/health"
Espera HTTP 200 y healthy: true, con una ejecución real de salud en AWS View y ningún pedido.
Inicia sesión con el usuario sintético preparado mediante el cliente público previsto y almacena la respuesta real de forma privada:
aws cognito-idp initiate-auth \
--client-id "$CLIENT_ID" \
--auth-flow USER_PASSWORD_AUTH \
--auth-parameters file://sign-in.json \
--query AuthenticationResult \
--output json > tokens.json
Extrae los tokens de acceso e ID sin imprimirlos:
jq -r '.AccessToken' tokens.json > access-token.txt
jq -r '.IdToken' tokens.json > id-token.txt
Usa únicamente el token de acceso para leer el nombre de usuario de la aplicación en vivo:
aws cognito-idp get-user \
--access-token "$(cat access-token.txt)" \
--query Username \
--output text
Espera labex-demo. AWS View muestra el usuario confirmado y el cliente preparados. La comprobación independiente valida el token realmente emitido y la identidad en vivo; no vuelve a autenticar por ti.
Exigir el autorizador en ambas rutas de pedidos
En este paso, configurarás el emisor y la audiencia previstos y aplicarás el mismo autorizador JWT a las lecturas y escrituras privadas.
Un autorizador JWT comprueba un token firmado antes de que API Gateway invoque una ruta protegida. El emisor es la identidad firmada del grupo de Cognito. La audiencia es el cliente de aplicación previsto: API Gateway valida aud cuando está presente y, en caso contrario, client_id. Construye el emisor a partir del ID real de grupo:
ISSUER="https://cognito-idp.us-east-1.amazonaws.com/$POOL_ID"
Usa un marcador de here-document sin comillas para que el shell expanda los dos ID en un archivo normal de configuración:
cat > jwt-config.json <<JSON
{
"Issuer": "$ISSUER",
"Audience": ["$CLIENT_ID"]
}
JSON
Crea un autorizador JWT. Las comillas simples conservan la fuente de identidad literal $request.header.Authorization en lugar de expandirla como variable del shell:
AUTH_ID=$(aws apigatewayv2 create-authorizer \
--api-id "$API_ID" \
--name OrdersUsers \
--authorizer-type JWT \
--identity-source '$request.header.Authorization' \
--jwt-configuration file://jwt-config.json \
--query AuthorizerId \
--output text)
Encuentra el ID generado de cada ruta de pedidos:
READ_ROUTE_ID=$(aws apigatewayv2 get-routes \
--api-id "$API_ID" \
--query "Items[?RouteKey=='GET /orders'].RouteId | [0]" \
--output text)
WRITE_ROUTE_ID=$(aws apigatewayv2 get-routes \
--api-id "$API_ID" \
--query "Items[?RouteKey=='POST /orders'].RouteId | [0]" \
--output text)
API Gateway no distingue universalmente tokens de acceso de tokens de ID. Estos tokens de acceso nativos del flujo de contraseña de Cognito contienen aws.cognito.signin.user.admin; exigir ese alcance rechaza el token de ID, que carece de él. El alcance indica acceso de autoservicio de usuarios de Cognito, no administración de AWS ni propiedad de pedidos. Este ejercicio no crea un alcance de negocio personalizado.
Exige JWT más ese alcance en la ruta de lectura:
aws apigatewayv2 update-route \
--api-id "$API_ID" \
--route-id "$READ_ROUTE_ID" \
--authorization-type JWT \
--authorizer-id "$AUTH_ID" \
--authorization-scopes aws.cognito.signin.user.admin \
--query '{Route:RouteKey,Authorization:AuthorizationType,Scopes:AuthorizationScopes}'
Aplica el mismo requisito a la ruta de escritura:
aws apigatewayv2 update-route \
--api-id "$API_ID" \
--route-id "$WRITE_ROUTE_ID" \
--authorization-type JWT \
--authorizer-id "$AUTH_ID" \
--authorization-scopes aws.cognito.signin.user.admin \
--query '{Route:RouteKey,Authorization:AuthorizationType,Scopes:AuthorizationScopes}'
La etapa preparada $default usa AutoDeploy, así que los cambios de rutas se aplican automáticamente. Lee de nuevo todos los tipos de ruta:
aws apigatewayv2 get-routes \
--api-id "$API_ID" \
--query 'Items[].{Route:RouteKey,Authorization:AuthorizationType}'
Espera JWT en ambas rutas de pedidos y NONE en GET /health. En AWS View, compara el emisor real, la audiencia prevista, los ID de autorizador de rutas y el alcance requerido. Crear únicamente un autorizador sin adjuntarlo a cada ruta privada no protege esas rutas.

Ejemplo: las dos rutas de pedidos comparten el autorizador y el alcance previstos, mientras la salud permanece pública. Los ID de recursos generados son distintos en tu VM.

Probar solicitudes aceptadas y rechazadas
En este paso, demostrarás acceso real de negocio para el usuario previsto y rechazo antes de Lambda para tokens ausentes o inadecuados.
Un token de portador autentica a quien lo presenta. Léelo desde el archivo privado de transporte dentro de la cabecera Authorization. Sigue usando curl -i para mostrar únicamente las cabeceras y el cuerpo de respuesta; no uses registro detallado de cabeceras de solicitud. Envía un pedido válido con cantidad 4:
curl -i -X POST "$API_URL/orders" -H "Authorization: Bearer $(cat access-token.txt)" -H 'Content-Type: application/json' --data '{"id":"signed-order","quantity":4}'
Espera HTTP 201 y total 1100 (4 × 250 + 100). Recupera el mismo pedido almacenado con el token de acceso:
curl -i "$API_URL/orders?id=signed-order" -H "Authorization: Bearer $(cat access-token.txt)"
Espera HTTP 200 con el mismo ID, cantidad y total calculado. Inspecciona el elemento nativo de forma independiente:
aws dynamodb get-item \
--table-name labex-a05-orders \
--key '{"id":{"S":"signed-order"}}' \
--consistent-read \
--query Item
Ahora repite la lectura sin token:
curl -i "$API_URL/orders?id=signed-order"
Espera HTTP 401 Unauthorized, sin ningún registro privado en la respuesta. Prueba también una escritura anónima:
curl -i -X POST "$API_URL/orders" -H 'Content-Type: application/json' --data '{"id":"reject-missing","quantity":9}'
También debe devolver 401 y no crear ningún elemento. Tanto las lecturas como las escrituras privadas exigen autorización.
El token de prueba suministrado con firma incorrecta tiene un byte de firma alterado. No se puede confiar en él aunque sus claims visibles parezcan plausibles:
curl -i -X POST "$API_URL/orders" -H "Authorization: Bearer $(cat bad-signature-token.txt)" -H 'Content-Type: application/json' --data '{"id":"reject-signature","quantity":9}'
Espera 401. Un token realmente emitido para otro cliente de aplicación tampoco es adecuado para esta API. Lee ese ID seguro de cliente, inicia sesión mediante él y extrae su token de acceso de forma privada:
WRONG_CLIENT_ID=$(jq -r '.wrong_client' scenario.json)
aws cognito-idp initiate-auth \
--client-id "$WRONG_CLIENT_ID" \
--auth-flow USER_PASSWORD_AUTH \
--auth-parameters file://sign-in.json \
--query AuthenticationResult \
--output json > wrong-client.json
jq -r '.AccessToken' wrong-client.json > wrong-client-token.txt
curl -i -X POST "$API_URL/orders" -H "Authorization: Bearer $(cat wrong-client-token.txt)" -H 'Content-Type: application/json' --data '{"id":"reject-client","quantity":9}'
Espera 401: una firma válida por sí sola no coincide con la audiencia configurada. Después usa el otro emisor de Cognito preparado, que tiene su propio usuario sintético y cliente público:
OTHER_POOL_ID=$(jq -r '.other.pool' scenario.json)
OTHER_CLIENT_ID=$(jq -r '.other.client' scenario.json)
aws cognito-idp initiate-auth \
--client-id "$OTHER_CLIENT_ID" \
--auth-flow USER_PASSWORD_AUTH \
--auth-parameters file://sign-in.json \
--query AuthenticationResult \
--output json > wrong-issuer.json
jq -r '.AccessToken' wrong-issuer.json > wrong-issuer-token.txt
curl -i -X POST "$API_URL/orders" -H "Authorization: Bearer $(cat wrong-issuer-token.txt)" -H 'Content-Type: application/json' --data '{"id":"reject-issuer","quantity":9}'
Espera 401 porque el emisor firmado difiere del grupo esperado. El token caducado de prueba suministrado está firmado con un exp que ya está en el pasado; prueba la regla de tiempo sin esperar a que caduque una sesión en vivo:
curl -i -X POST "$API_URL/orders" -H "Authorization: Bearer $(cat expired-token.txt)" -H 'Content-Type: application/json' --data '{"id":"reject-expired","quantity":9}'
Espera 401. Un token de ID está firmado para este cliente, pero carece del alcance de token de acceso requerido por la ruta:
curl -i -X POST "$API_URL/orders" -H "Authorization: Bearer $(cat id-token.txt)" -H 'Content-Type: application/json' --data '{"id":"reject-id-token","quantity":9}'
Espera 403 por alcance insuficiente. La firma, el emisor, la audiencia y la vigencia del token son necesarios, pero no proporcionan permisos ausentes.
Comprueba que la salud permanece pública y que solo se almacenó la escritura válida de negocio:
curl -i "$API_URL/health"
aws dynamodb scan --table-name labex-a05-orders --query Items
Espera salud 200 y exactamente signed-order con total 1100. En AWS View, compara API requests reales con CloudWatch Logs: la autorización rechazada tiene un resultado 401/403 de la puerta de enlace y ninguna ejecución de función coincidente. Las solicitudes privadas correctas tienen eventos y respuestas reales de Lambda, con las cabeceras de credenciales excluidas. Estos resultados de ejecución y API y los datos nativos, no capturas ni marcadores locales de éxito, son la evidencia de evaluación.

Ejemplo: las escrituras y lecturas válidas de pedidos devolvieron 201/200; los tokens inadecuados devolvieron 401/403. La lista de ejecuciones de función y la tabla nativa confirman de forma independiente que las solicitudes rechazadas no realizaron ninguna escritura de negocio.
Eliminar las rutas protegidas y los recursos del laboratorio
En este paso, eliminarás la API, los componentes de identidad y los datos del laboratorio y conservarás únicamente las referencias ajenas a él.
Tu inventario consta de esta API y su autorizador, función y grupo de registros, grupo de registros de solicitudes de la puerta de enlace, tabla de pedidos, política OrdersData del rol, grupo principal (dos clientes y un usuario), grupo del otro emisor (un cliente y un usuario) y archivos privados de entrada y respuesta. El grupo de referencia, la tabla, los registros de referencia y la política FunctionLogs del rol son ajenos al laboratorio y deben permanecer.
Elimina la API, incluidos su autorizador, rutas, integración y etapa:
aws apigatewayv2 delete-api --api-id "$API_ID"
aws lambda delete-function --function-name labex-a05-orders-api
aws logs delete-log-group --log-group-name /aws/lambda/labex-a05-orders-api
aws logs delete-log-group --log-group-name /aws/apigateway/labex-a05
aws dynamodb delete-table \
--table-name labex-a05-orders \
--query TableDescription.TableName \
--output text
aws iam delete-role-policy --role-name labex-a05-execution --policy-name OrdersData
Elimina los usuarios sintéticos y cada cliente de aplicación del laboratorio y después sus grupos:
aws cognito-idp admin-delete-user --user-pool-id "$POOL_ID" --username labex-demo
aws cognito-idp delete-user-pool-client --user-pool-id "$POOL_ID" --client-id "$CLIENT_ID"
aws cognito-idp delete-user-pool-client \
--user-pool-id "$POOL_ID" \
--client-id "$WRONG_CLIENT_ID"
aws cognito-idp delete-user-pool --user-pool-id "$POOL_ID"
aws cognito-idp admin-delete-user --user-pool-id "$OTHER_POOL_ID" --username labex-demo
aws cognito-idp delete-user-pool-client \
--user-pool-id "$OTHER_POOL_ID" \
--client-id "$OTHER_CLIENT_ID"
aws cognito-idp delete-user-pool --user-pool-id "$OTHER_POOL_ID"
Consulta correctamente los inventarios para demostrar ausencia; los errores de red o autenticación no son evidencia de limpieza:
aws apigatewayv2 get-apis --query 'Items[].Name'
aws lambda list-functions --query 'Functions[].FunctionName'
aws cognito-idp list-user-pools --max-results 60 --query 'UserPools[].Name'
aws dynamodb list-tables --query TableNames
Espera listas vacías de API y funciones, únicamente labex-a05-reference en las listas de grupos y tablas y datos y registros de referencia preservados en AWS View. Elimina únicamente los archivos privados enumerados:
rm -f tokens.json access-token.txt id-token.txt wrong-client.json wrong-client-token.txt wrong-issuer.json wrong-issuer-token.txt expired-token.txt future-iat-token.txt future-nbf-token.txt bad-signature-token.txt sign-in.json
Eliminar únicamente archivos locales no constituye una revocación general de JWT del lado del servidor. También se eliminaron los usuarios, clientes, grupos sintéticos y la API del laboratorio. Los componentes de referencia, la revocación final de credenciales y el apagado de la VM quedan para la limpieza del autor después de estas comprobaciones de recursos de solo lectura.
Resumen
Configuraste un autorizador JWT de Cognito y lo exigiste en lecturas y escrituras privadas conservando la salud pública. Probaste solicitudes reales aceptadas y rechazos por tokens ausentes, alterados, de otro cliente, de otro emisor, caducados y de ID. Relacionaste los resultados reales de la puerta de enlace con las ejecuciones nativas de Lambda y los datos almacenados; después eliminaste los recursos de API, identidad y datos del laboratorio y los archivos privados conservando las referencias. La autorización JWT no implementó propiedad por pedido.



