Controlar o acesso a APIs HTTP com autorizadores do AWS Lambda - Amazon API Gateway

Controlar o acesso a APIs HTTP com autorizadores do AWS Lambda

Use um autorizador do Lambda para utilizar uma função do Lambda a fim de controlar o acesso à sua API HTTP. Depois, quando um cliente chamar a API, o API Gateway invocará sua função do Lambda. O API Gateway usará a resposta da função do Lambda para determinar se o cliente pode acessar sua API.

Versão do formato da carga útil

A versão do formato de carga do autorizador especifica o formato dos dados que o API Gateway envia a um autorizador do Lambda e como o API Gateway interpreta a resposta do Lambda. Por padrão, se você não especificar uma versão de formato de carga útil, o AWS Management Console usará a versão mais recente. Se você criar um autorizador do Lambda usando a AWS CLI, o AWS CloudFormation ou um SDK, será necessário especificar um authorizerPayloadFormatVersion. Os valores suportados são 1.0 e 2.0.

Se precisar de compatibilidade com APIs REST, use a versão 1.0.

Os exemplos a seguir mostram a estrutura de cada versão do formato de carga útil.

2.0
{ "version": "2.0", "type": "REQUEST", "routeArn": "arn:aws:execute-api:us-east-1:123456789012:abcdef123/test/GET/request", "identitySource": ["user1", "123"], "routeKey": "$default", "rawPath": "/my/path", "rawQueryString": "parameter1=value1&parameter1=value2&parameter2=value", "cookies": ["cookie1", "cookie2"], "headers": { "header1": "value1", "header2": "value2" }, "queryStringParameters": { "parameter1": "value1,value2", "parameter2": "value" }, "requestContext": { "accountId": "123456789012", "apiId": "api-id", "authentication": { "clientCert": { "clientCertPem": "CERT_CONTENT", "subjectDN": "www.example.com", "issuerDN": "Example issuer", "serialNumber": "1", "validity": { "notBefore": "May 28 12:30:02 2019 GMT", "notAfter": "Aug 5 09:36:04 2021 GMT" } } }, "domainName": "id.execute-api.us-east-1.amazonaws.com", "domainPrefix": "id", "http": { "method": "POST", "path": "/my/path", "protocol": "HTTP/1.1", "sourceIp": "IP", "userAgent": "agent" }, "requestId": "id", "routeKey": "$default", "stage": "$default", "time": "12/Mar/2020:19:03:58 +0000", "timeEpoch": 1583348638390 }, "pathParameters": { "parameter1": "value1" }, "stageVariables": { "stageVariable1": "value1", "stageVariable2": "value2" } }
1.0
{ "version": "1.0", "type": "REQUEST", "methodArn": "arn:aws:execute-api:us-east-1:123456789012:abcdef123/test/GET/request", "identitySource": "user1,123", "authorizationToken": "user1,123", "resource": "/request", "path": "/request", "httpMethod": "GET", "headers": { "X-AMZ-Date": "20170718T062915Z", "Accept": "*/*", "HeaderAuth1": "headerValue1", "CloudFront-Viewer-Country": "US", "CloudFront-Forwarded-Proto": "https", "CloudFront-Is-Tablet-Viewer": "false", "CloudFront-Is-Mobile-Viewer": "false", "User-Agent": "..." }, "queryStringParameters": { "QueryString1": "queryValue1" }, "pathParameters": {}, "stageVariables": { "StageVar1": "stageValue1" }, "requestContext": { "path": "/request", "accountId": "123456789012", "resourceId": "05c7jb", "stage": "test", "requestId": "...", "identity": { "apiKey": "...", "sourceIp": "...", "clientCert": { "clientCertPem": "CERT_CONTENT", "subjectDN": "www.example.com", "issuerDN": "Example issuer", "serialNumber": "a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1", "validity": { "notBefore": "May 28 12:30:02 2019 GMT", "notAfter": "Aug 5 09:36:04 2021 GMT" } } }, "resourcePath": "/request", "httpMethod": "GET", "apiId": "abcdef123" } }

Formato de resposta do autorizador do Lambda

A versão do formato de carga também determina a estrutura da resposta que deve ser retornada pela função do Lambda.

Resposta de função do Lambda para o formato 1.0

Se você escolher a versão de formato 1.0, os autorizadores do Lambda deverão retornar uma política do IAM que permita ou negue acesso à sua rota da API. É possível usar a sintaxe de política do IAM padrão na política. Para obter políticas demonstrativas do IAM, consulte Controlar o acesso para chamar uma API. É possível transmitir propriedades de contexto para integrações do Lambda ou logs de acesso usando $context.authorizer.property. O objeto context é opcional e claims é um espaço reservado que não pode ser usado como objeto de contexto. Para saber mais, consulte Personalizar logs de acesso à API HTTP.

{ "principalId": "abcdef", // The principal user identification associated with the token sent by the client. "policyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": "execute-api:Invoke", "Effect": "Allow|Deny", "Resource": "arn:aws:execute-api:{regionId}:{accountId}:{apiId}/{stage}/{httpVerb}/[{resource}/[{child-resources}]]" } ] }, "context": { "exampleKey": "exampleValue" } }

Resposta de função do Lambda para o formato 2.0

Se você escolher a versão de formato 2.0, poderá retornar um valor booliano ou uma política do IAM que use a sintaxe de política padrão do IAM da sua função do Lambda. Para retornar um valor booliano, ative respostas simples para o autorizador. Os exemplos a seguir demonstram o formato que você deve codificar para ser retornado pela sua função do Lambda. O objeto context é opcional. É possível transmitir propriedades de contexto para integrações do Lambda ou logs de acesso usando $context.authorizer.property. Para saber mais, consulte Personalizar logs de acesso à API HTTP.

Simple response
{ "isAuthorized": true/false, "context": { "exampleKey": "exampleValue" } }
IAM policy
{ "principalId": "abcdef", // The principal user identification associated with the token sent by the client. "policyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": "execute-api:Invoke", "Effect": "Allow|Deny", "Resource": "arn:aws:execute-api:{regionId}:{accountId}:{apiId}/{stage}/{httpVerb}/[{resource}/[{child-resources}]]" } ] }, "context": { "exampleKey": "exampleValue" } }

Funções demonstrativas do autorizador do Lambda

As funções demonstrativas do Lambda Node.js a seguir mostram os formatos de resposta que você precisa retornar de sua função do Lambda para a versão de formato de carga 2.0.

Simple response - Node.js
export const handler = async(event) => { let response = { "isAuthorized": false, "context": { "stringKey": "value", "numberKey": 1, "booleanKey": true, "arrayKey": ["value1", "value2"], "mapKey": {"value1": "value2"} } }; if (event.headers.authorization === "secretToken") { console.log("allowed"); response = { "isAuthorized": true, "context": { "stringKey": "value", "numberKey": 1, "booleanKey": true, "arrayKey": ["value1", "value2"], "mapKey": {"value1": "value2"} } }; } return response; };
Simple response - Python
import json def lambda_handler(event, context): response = { "isAuthorized": False, "context": { "stringKey": "value", "numberKey": 1, "booleanKey": True, "arrayKey": ["value1", "value2"], "mapKey": {"value1": "value2"} } } try: if (event["headers"]["authorization"] == "secretToken"): response = { "isAuthorized": True, "context": { "stringKey": "value", "numberKey": 1, "booleanKey": True, "arrayKey": ["value1", "value2"], "mapKey": {"value1": "value2"} } } print('allowed') return response else: print('denied') return response except BaseException: print('denied') return response
IAM policy - Node.js
export const handler = async(event) => { if (event.headers.authorization == "secretToken") { console.log("allowed"); return { "principalId": "abcdef", // The principal user identification associated with the token sent by the client. "policyDocument": { "Version": "2012-10-17", "Statement": [{ "Action": "execute-api:Invoke", "Effect": "Allow", "Resource": event.routeArn }] }, "context": { "stringKey": "value", "numberKey": 1, "booleanKey": true, "arrayKey": ["value1", "value2"], "mapKey": { "value1": "value2" } } }; } else { console.log("denied"); return { "principalId": "abcdef", // The principal user identification associated with the token sent by the client. "policyDocument": { "Version": "2012-10-17", "Statement": [{ "Action": "execute-api:Invoke", "Effect": "Deny", "Resource": event.routeArn }] }, "context": { "stringKey": "value", "numberKey": 1, "booleanKey": true, "arrayKey": ["value1", "value2"], "mapKey": { "value1": "value2" } } }; } };
IAM policy - Python
import json def lambda_handler(event, context): response = { # The principal user identification associated with the token sent by # the client. "principalId": "abcdef", "policyDocument": { "Version": "2012-10-17", "Statement": [{ "Action": "execute-api:Invoke", "Effect": "Deny", "Resource": event["routeArn"] }] }, "context": { "stringKey": "value", "numberKey": 1, "booleanKey": True, "arrayKey": ["value1", "value2"], "mapKey": {"value1": "value2"} } } try: if (event["headers"]["authorization"] == "secretToken"): response = { # The principal user identification associated with the token # sent by the client. "principalId": "abcdef", "policyDocument": { "Version": "2012-10-17", "Statement": [{ "Action": "execute-api:Invoke", "Effect": "Allow", "Resource": event["routeArn"] }] }, "context": { "stringKey": "value", "numberKey": 1, "booleanKey": True, "arrayKey": ["value1", "value2"], "mapKey": {"value1": "value2"} } } print('allowed') return response else: print('denied') return response except BaseException: print('denied') return response

Fontes de identidade

Também é possível especificar fontes de identidade para um autorizador do Lambda. As fontes de identidade especificam a localização dos dados necessários para autorizar uma solicitação. Por exemplo, é possível especificar valores de cabeçalho ou string de consulta como origens de identidade. Se você especificar fontes de identidade, os clientes deverão incluí-las na solicitação. Se a solicitação do cliente não incluir as fontes de identidade, o API Gateway não invocará o autorizador do Lambda, e o cliente receberá um erro 401.

A tabela a seguir descreve as fontes de identidade compatíveis com um autorizador do Lambda.

Tipo

Exemplo

Observações

Valor de cabeçalho $request.header.nome Nomes de cabeçalhos não diferenciam maiúsculas de minúsculas.
Valor da string de consulta $request.querystring.nome Os nomes de strings de consulta diferenciam maiúsculas e minúsculas
Variável de contexto $context.variableName O valor de uma variável de contexto compatível.
Variável de estágio $stageVariables.variableName O valor de uma variável de estágio.

Armazenar em cache respostas do autorizador

É possível habilitar o armazenamento em cache para um autorizador do Lambda especificando um authorizerResultTtlInSeconds. Quando o cache está habilitado para um autorizador, o API Gateway usa as fontes de identidade do autorizador como a chave de cache. Se um cliente especificar os mesmos parâmetros em fontes de identidade dentro do TTL configurado, o API Gateway usará o resultado do autorizador em cache em vez de invocar sua função do Lambda.

Para habilitar o cache, seu autorizador deve ter pelo menos uma fonte de identidade.

Se você habilitar respostas simples para um autorizador, a resposta do autorizador permitirá ou negará totalmente todas as solicitações de API que correspondam aos valores da fonte de identidade armazenados em cache. Para obter permissões mais granulares, desative respostas simples e retorne uma política do IAM.

Por padrão, o API Gateway usa a resposta do autorizador em cache para todas as rotas de uma API que usam o autorizador. Para armazenar em cache as respostas por rota, adicione $context.routeKey às fontes de identidade do seu autorizador.

Criar um autorizador do Lambda

Ao criar um autorizador do Lambda, especifique a função do Lambda a ser usada pelo API Gateway. É necessário conceder permissão do API Gateway para invocar a função do Lambda usando a política de recursos da função ou uma função do IAM. Para este exemplo, atualizamos a política de recursos para a função para que ela conceda ao API Gateway permissão para invocar nossa função do Lambda.

aws apigatewayv2 create-authorizer \ --api-id abcdef123 \ --authorizer-type REQUEST \ --identity-source '$request.header.Authorization' \ --name lambda-authorizer \ --authorizer-uri 'arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:123456789012:function:my-function/invocations' \ --authorizer-payload-format-version '2.0' \ --enable-simple-responses

O comando a seguir concede ao API Gateway permissão para invocar sua função do Lambda. Se o API Gateway não tiver permissão para invocar sua função, os clientes receberão um 500 Internal Server Error.

aws lambda add-permission \ --function-name my-authorizer-function \ --statement-id apigateway-invoke-permissions-abc123 \ --action lambda:InvokeFunction \ --principal apigateway.amazonaws.com \ --source-arn "arn:aws:execute-api:us-west-2:123456789012:api-id/authorizers/authorizer-id"

Depois de criar um autorizador e conceder ao API Gateway permissão para invocá-lo, atualize sua rota para usar o autorizador.

aws apigatewayv2 update-route \ --api-id abcdef123 \ --route-id acd123 \ --authorization-type CUSTOM \ --authorizer-id def123

Solução de problemas em autorizadores do Lambda

Se o API Gateway não conseguir invocar o autorizador do Lambda, ou se o autorizador do Lambda retornar uma resposta em um formato inválido, os clientes receberão um arquivo 500 Internal Server Error.

Para solucionar erros, habilite o registro em log de acesso para o estágio da API. Inclua a variável de registro em log $context.authorizer.error em seu formato de log.

Se os logs indicarem que o API Gateway não tem permissão para invocar sua função, atualize a política de recursos da função ou forneça uma função do IAM para conceder ao API Gateway permissão para invocar o autorizador.

Se os logs indicarem que a função do Lambda retorna uma resposta inválida, verifique se a função do Lambda retorna uma resposta no formato necessário.