Introdução
A API de pedidos deve exigir login para leituras e gravações, mantendo a saúde pública. Você anexará verificações de tokens a ambas as rotas de pedidos, testará solicitações aceitas e rejeitadas e confirmará que chamadas rejeitadas deixam os dados de negócio inalterados.
Conclua primeiro Adicione Login à Aplicação com Cognito e Construa e Valide uma API de Pedidos. Este ambiente novo fornece um backend público funcional e recursos sintéticos de identidade; recursos e tokens anteriores não são reutilizados.
Relação com as certificações
Este laboratório oferece prática nos seguintes tópicos de exame.
- Solutions Architect – Associate (SAA-C03) · Tarefa 1.2: Autorização API baseada em tokens e testes dos limites de acesso.
- Developer – Associate (DVA-C02) · Tarefa 2.1: Autorização API baseada em tokens e testes dos limites de acesso.
- Security – Specialty (SCS-C03) · Tarefa 4.1: Prática dos fundamentos: Autorização API baseada em tokens e testes dos limites de acesso.
Inspecione as Rotas Públicas e Entre
Nesta etapa, você estabelecerá o estado atual das rotas públicas e obterá um token de acesso real para o cliente de aplicação pretendido.
Entre no diretório de trabalho e mantenha os arquivos de tokens recém-criados privados:
cd /home/labex/project
umask 077
Abra AWS View ao lado do Terminal para comparar solicitações da API, execuções da função e pedidos armazenados. Logs de solicitações do gateway e logs de execução Lambda são separados: um token rejeitado deve interromper a solicitação antes de a função executar.
Leia o inventário seguro dos recursos fornecidos. Ele identifica o pool/cliente pretendido, um cliente diferente, um emissor diferente e o pool de referência não relacionado:
cat scenario.json
Use jq -r para selecionar os IDs principais, armazenando a saída com a substituição de comando $(...):
POOL_ID=$(jq -r '.main.pool' scenario.json)
CLIENT_ID=$(jq -r '.main.client' scenario.json)
Encontre a HTTP API preparada por nome e registre seu ID gerado para a limpeza:
API_ID=$(aws apigatewayv2 get-apis \
--query "Items[?Name=='labex-a05'].ApiId | [0]" \
--output text)
printf '%s\n' "$API_ID" > api-id.txt
Inspecione os tipos de autorização das rotas:
aws apigatewayv2 get-routes \
--api-id "$API_ID" \
--query 'Items[].{Route:RouteKey,Authorization:AuthorizationType}'
As três rotas inicialmente usam NONE. O diretório de usuários sozinho não protegeu a API. Construa seu endereço no ambiente de trabalho:
API_URL="http://127.0.0.1:8081/api/$API_ID"
curl -i "$API_URL/health"
Espere HTTP 200 e healthy: true, com uma execução real de saúde no AWS View e nenhum pedido.
Entre com o usuário sintético preparado pelo cliente público pretendido, armazenando a resposta 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
Extraia os tokens de acesso e ID sem imprimi-los:
jq -r '.AccessToken' tokens.json > access-token.txt
jq -r '.IdToken' tokens.json > id-token.txt
Use apenas o token de acesso para ler o nome de usuário ativo da aplicação:
aws cognito-idp get-user \
--access-token "$(cat access-token.txt)" \
--query Username \
--output text
Espere labex-demo. O AWS View mostra o usuário confirmado e o cliente preparados. A verificação independente valida o token realmente emitido e a identidade ativa; ela não autentica novamente por você.
Exija o Autorizador em Ambas as Rotas de Pedidos
Nesta etapa, você configurará o emissor e o público-alvo pretendidos e aplicará o mesmo autorizador JWT às leituras e gravações privadas.
Um autorizador JWT verifica um token assinado antes de o API Gateway invocar uma rota protegida. O emissor (issuer) é a identidade assinada do pool Cognito. O público-alvo (audience) é o cliente de aplicação pretendido: o API Gateway valida aud quando presente; caso contrário, valida client_id. Construa o emissor a partir do ID real do pool:
ISSUER="https://cognito-idp.us-east-1.amazonaws.com/$POOL_ID"
Use um marcador de heredoc sem aspas para que o shell expanda os dois IDs em um arquivo comum de configuração:
cat > jwt-config.json <<JSON
{
"Issuer": "$ISSUER",
"Audience": ["$CLIENT_ID"]
}
JSON
Crie um autorizador JWT. As aspas simples preservam a origem literal de identidade $request.header.Authorization, em vez de expandi-la como uma variável do 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)
Encontre o ID gerado de cada rota 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)
O API Gateway não distingue universalmente tokens de acesso de tokens de ID. Estes tokens de acesso nativos do fluxo de senha Cognito carregam aws.cognito.signin.user.admin; exigir esse escopo rejeita o token de ID, que não o tem. O escopo indica acesso de autoatendimento do usuário do Cognito, não administração AWS nem propriedade de pedidos. Este exercício não cria um escopo de negócio personalizado.
Exija JWT mais esse escopo na rota de leitura:
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}'
Aplique o mesmo requisito à rota de gravação:
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}'
O estágio $default preparado usa AutoDeploy, portanto as mudanças de rotas são aplicadas automaticamente. Leia novamente todos os tipos de rotas:
aws apigatewayv2 get-routes \
--api-id "$API_ID" \
--query 'Items[].{Route:RouteKey,Authorization:AuthorizationType}'
Espere JWT em ambas as rotas de pedidos e NONE em GET /health. No AWS View, compare o emissor real, o público-alvo pretendido, os IDs dos autorizadores das rotas e o escopo exigido. Apenas criar um autorizador sem anexá-lo a cada rota privada não protege essas rotas.

Exemplo: as duas rotas de pedidos compartilham o autorizador e o escopo pretendidos, enquanto a saúde permanece pública. Os IDs gerados dos recursos são diferentes em sua VM.

Teste Solicitações Aceitas e Rejeitadas
Nesta etapa, você comprovará o acesso real de negócio para o usuário pretendido e a rejeição antes do Lambda para tokens ausentes ou inadequados.
Um token bearer autentica quem o apresenta. Leia-o do arquivo privado de transporte dentro do cabeçalho Authorization. Continue usando curl -i para mostrar apenas cabeçalhos e corpo da resposta; não use o registro detalhado de cabeçalhos da solicitação. Envie um pedido válido com quantidade 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}'
Espere HTTP 201 e total 1100 (4 × 250 + 100). Recupere o mesmo pedido armazenado com o token de acesso:
curl -i "$API_URL/orders?id=signed-order" -H "Authorization: Bearer $(cat access-token.txt)"
Espere HTTP 200 com o mesmo ID, quantidade e total calculado. Inspecione o item nativo de forma independente:
aws dynamodb get-item \
--table-name labex-a05-orders \
--key '{"id":{"S":"signed-order"}}' \
--consistent-read \
--query Item
Agora repita a leitura sem um token:
curl -i "$API_URL/orders?id=signed-order"
Espere HTTP 401 Unauthorized, sem registro privado na resposta. Teste também uma gravação anônima:
curl -i -X POST "$API_URL/orders" -H 'Content-Type: application/json' --data '{"id":"reject-missing","quantity":9}'
Ela também deve retornar 401 e não criar nenhum item. Tanto leituras quanto gravações privadas exigem autorização.
O token de teste fornecido com assinatura inválida tem um byte de assinatura alterado. Ele não é confiável mesmo que seus dados visíveis de claims pareçam plausíveis:
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}'
Espere 401. Um token genuinamente emitido para outro cliente de aplicação também é inadequado para esta API. Leia esse ID seguro de cliente, entre por ele e extraia seu token de acesso 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}'
Espere 401: uma assinatura válida sozinha não corresponde ao público-alvo configurado. Em seguida, use o emissor Cognito diferente preparado, que tem seu próprio usuário sintético e 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}'
Espere 401 porque o emissor assinado difere do pool esperado. O token expirado fornecido é assinado com um exp já no passado; ele testa a regra de tempo sem esperar que uma sessão ativa expire:
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}'
Espere 401. Um token de ID é assinado para este cliente, mas não tem o escopo exigido pela rota para o token de acesso:
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}'
Espere 403 por escopo insuficiente. Assinatura, emissor, público-alvo e validade temporal do token são necessários, mas não fornecem permissões ausentes.
Verifique que a saúde permanece pública e que apenas a gravação válida de negócio foi armazenada:
curl -i "$API_URL/health"
aws dynamodb scan --table-name labex-a05-orders --query Items
Espere saúde 200 e exatamente signed-order com total 1100. No AWS View, compare API requests reais com CloudWatch Logs: uma autorização rejeitada tem resultado 401/403 no gateway e nenhuma execução correspondente da função. Solicitações privadas bem-sucedidas têm eventos e respostas reais Lambda, com cabeçalhos de credenciais excluídos. Esses resultados de execução/API e os dados nativos, não capturas de tela nem marcadores locais de sucesso, são as evidências de avaliação.

Exemplo: gravações e leituras válidas de pedidos retornaram 201/200; tokens inadequados retornaram 401/403. A lista de execuções da função e a tabela nativa confirmam de forma independente que solicitações rejeitadas não fizeram gravações de negócio.
Remova Rotas Protegidas e Recursos do Laboratório
Nesta etapa, você removerá a API, os recursos de identidade e os dados do laboratório, preservando apenas as referências não relacionadas.
Seu inventário consiste nesta API e seu autorizador, função/grupo de logs, grupo de logs de solicitações do gateway, tabela de pedidos, política OrdersData da função IAM, pool principal (dois clientes e um usuário), pool do outro emissor (um cliente e um usuário) e arquivos privados de entrada/resposta. O pool de referência, a tabela, os logs de referência e a política FunctionLogs da função IAM são não relacionados e devem permanecer.
Exclua a API, incluindo seu autorizador, rotas, integração e estágio:
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
Remova os usuários sintéticos e cada cliente de aplicação do laboratório e, depois, seus pools:
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"
Consulte os inventários com sucesso para comprovar a ausência; erros de rede/autenticação não são evidências de limpeza:
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
Espere listas vazias de APIs/funções, apenas labex-a05-reference nas listas de pools e tabelas e os dados/logs de referência preservados no AWS View. Remova apenas os arquivos privados listados:
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
Excluir arquivos locais sozinho não é revogação geral de JWT no servidor. Os usuários/clientes/pools sintéticos do laboratório e a API também foram excluídos. Os recursos de referência, a revogação final de credenciais e o encerramento da VM permanecem para a limpeza do autor após essas verificações somente de leitura dos recursos.
Resumo
Você configurou um autorizador JWT Cognito e o exigiu em leituras e gravações privadas, preservando a saúde pública. Você testou solicitações reais aceitas e rejeições de tokens ausentes, alterados, de cliente errado, de emissor errado, expirados e de ID. Você correlacionou resultados reais do gateway com execuções nativas Lambda e dados armazenados e removeu os recursos de API/identidade/dados do laboratório e os arquivos privados, preservando as referências. A autorização JWT não implementou a propriedade por pedido.



