Por que meu recurso de proxy do API Gateway com um autorizador do Lambda que tem o armazenamento em cache ativado está retornando erros de HTTP 403 “O usuário não está autorizado a acessar este recurso”?

6 minuto de leitura
0

Meu recurso de proxy do Amazon API Gateway com um autorizador do AWS Lambda que tem o armazenamento em cache ativado retorna a seguinte mensagem de erro HTTP 403: “O usuário não está autorizado a acessar este recurso”.

Breve descrição

Observação: O API Gateway pode retornar erros 403 O usuário não está autorizado a acessar este recurso por vários motivos. Este artigo aborda somente erros 403 relacionados aos recursos de proxy do API Gateway com um autorizador do Lambda que tem o armazenamento em cache ativado. Para obter informações sobre como solucionar outros tipos de erros 403, consulte Como soluciono erros “HTTP 403 Forbidden” do API Gateway?

A saída de um autorizador do Lambda retorna uma política do AWS Identity and Access Management (AWS IAM) para o API Gateway. A política do IAM inclui um elemento “Recurso” de API explícito do API Gateway, no seguinte formato:

"arn:aws:execute-api:<region>:<account>:<API_id>/<stage>/<http-method>/[<resource-path-name>/[<child-resources-path>]"

Quando o armazenamento em cache da autorização é ativado em um autorizador do Lambda, a política do IAM retornada é armazenada em cache. A política do IAM armazenada em cache é então aplicada a qualquer solicitação adicional de API feita dentro do período de vida útil (TTL) especificado do armazenamento em cache.

Se a API tiver um recurso de proxy com uma variável de caminho gananciosa de {proxy+}, a primeira autorização será bem-sucedida. Qualquer solicitação de API adicional feita em um caminho diferente dentro do período de TTL do armazenamento em cache falhará e retornará o seguinte erro:

“mensagem”: “O usuário não está autorizado a acessar este recurso”

As solicitações adicionais falham porque os caminhos não correspondem ao elemento “Recurso” de API explícito do API Gateway definido na política do IAM armazenada em cache.

Para resolver o problema, você pode modificar o código da função do autorizador do Lambda para retornar um recurso curinga (*/*) na saída. Para obter mais informações, consulte Recursos e condições para ações do Lambda.

Observação: Para ativar o armazenamento em cache do autorizador, seu autorizador deve retornar uma política aplicável a todos os métodos em um API Gateway. O código da função do autorizador do Lambda deve retornar um recurso curinga (*/*) na saída para permitir todos os recursos. A política de armazenamento em cache espera que o mesmo caminho de recurso seja armazenado em cache, a menos que você tenha feito a mesma solicitação duas vezes no mesmo caminho de recurso.

Resolução

Observação: Modifique os exemplos de trechos de código da função do autorizador do Lambda neste artigo para adequá-los ao seu caso de uso.

Nas configurações de exemplo a seguir, as funções do Lambda extraem o valor id do API Gateway do Amazon Resource Name (ARN) do método (“event.methodArn”). Em seguida, as funções definem uma variável curinga “Recurso” combinando os caminhos do ARN do método com o valor id do API e um curinga (*/*).

Exemplo de código de função do autorizador do Lambda baseado em token que retorna uma variável curinga “Recurso”

exports.handler =  function(event, context, callback) {
    var token = event.authorizationToken;
    var tmp = event.methodArn.split(':');
    var apiGatewayArnTmp = tmp[5].split('/');

    // Create wildcard resource
    var resource = tmp[0] + ":" + tmp[1] + ":" + tmp[2] + ":" + tmp[3] + ":" + tmp[4] + ":" + apiGatewayArnTmp[0] + '/*/*';
    switch (token) {
        case 'allow':
            callback(null, generatePolicy('user', 'Allow', resource));
            break;
        case 'deny':
            callback(null, generatePolicy('user', 'Deny', resource));
            break;
        case 'unauthorized':
            callback("Unauthorized");   // Return a 401 Unauthorized response
            break;
        default:
            callback("Error: Invalid token"); // Return a 500 Invalid token response
    }
};
// Help function to generate an IAM policy
var generatePolicy = function(principalId, effect, resource) {
    var authResponse = {};

    authResponse.principalId = principalId;
    if (effect && resource) {
        var policyDocument = {};
        policyDocument.Version = '2012-10-17';
        policyDocument.Statement = [];
        var statementOne = {};
        statementOne.Action = 'execute-api:Invoke';
        statementOne.Effect = effect;
        statementOne.Resource = resource;
        policyDocument.Statement[0] = statementOne;
        authResponse.policyDocument = policyDocument;
    }

    // Optional output with custom properties of the String, Number or Boolean type.
    authResponse.context = {
        "stringKey": "stringval",
        "numberKey": 123,
        "booleanKey": true
    };
    return authResponse;
}

Exemplo de código de função do autorizador do Lambda baseado em parâmetros de solicitação que retorna uma variável curinga “Recurso”

exports.handler = function(event, context, callback) {        

    // Retrieve request parameters from the Lambda function input:
    var headers = event.headers;
    var queryStringParameters = event.queryStringParameters;
    var pathParameters = event.pathParameters;
    var stageVariables = event.stageVariables;

    // Parse the input for the parameter values
    var tmp = event.methodArn.split(':');
    var apiGatewayArnTmp = tmp[5].split('/');

    // Create wildcard resource
    var resource = tmp[0] + ":" + tmp[1] + ":" + tmp[2] + ":" + tmp[3] + ":" + tmp[4] + ":" + apiGatewayArnTmp[0] + '/*/*';
    console.log("resource: " + resource);
    // if (apiGatewayArnTmp[3]) {
    //     resource += apiGatewayArnTmp[3];
    // }

    // Perform authorization to return the Allow policy for correct parameters and
    // the 'Unauthorized' error, otherwise.
    var authResponse = {};
    var condition = {};
    condition.IpAddress = {};


    if (headers.headerauth1 === "headerValue1"
        && queryStringParameters.QueryString1 === "queryValue1"
        && stageVariables.StageVar1 === "stageValue1") {
        callback(null, generateAllow('me', resource));
    }  else {
        callback("Unauthorized");
    }
}

// Help function to generate an IAM policy
var generatePolicy = function(principalId, effect, resource) {
    // Required output:
    console.log("Resource in generatePolicy(): " + resource);
    var authResponse = {};
    authResponse.principalId = principalId;
    if (effect && resource) {
        var policyDocument = {};
        policyDocument.Version = '2012-10-17'; // default version
        policyDocument.Statement = [];
        var statementOne = {};
        statementOne.Action = 'execute-api:Invoke'; // default action
        statementOne.Effect = effect;
        statementOne.Resource = resource;
        console.log("***Resource*** " + resource);
        policyDocument.Statement[0] = statementOne;
        console.log("***Generated Policy*** ");
        console.log(policyDocument);
        authResponse.policyDocument = policyDocument;
    }
    // Optional output with custom properties of the String, Number or Boolean type.
    authResponse.context = {
        "stringKey": "stringval",
        "numberKey": 123,
        "booleanKey": true
    };

    return authResponse;
}

var generateAllow = function(principalId, resource) {
    return generatePolicy(principalId, 'Allow', resource);
}

var generateDeny = function(principalId, resource) {
    return generatePolicy(principalId, 'Deny', resource);
}

Para obter mais informações sobre como editar o código da função do Lambda, consulte Implantar funções do Lambda definidas como arquivos .zip.

Informações relacionadas

Testando funções do Lambda no console