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

Data da última atualização: 04/02/2022

Meu recurso de proxy do Amazon API Gateway com um autorizador do AWS Lambda com o cache ativado retorna a seguinte mensagem de erro HTTP 403: “O usuário não está autorizado a acessar este recurso”. Por que isso está acontecendo e como soluciono o erro?

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 os erros 403 relacionados aos recursos de proxy do API Gateway com um autorizador do Lambda com o cache ativado somente. Para obter informações sobre como solucionar problemas de outros tipos de erros 403, consulte Como solucionar problemas de erros HTTP 403 do API Gateway?

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

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

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

Se a API tiver um recurso de proxy com uma variável de caminho forçado de {proxy+}, a primeira autorização será bem-sucedida. Quaisquer solicitações de API adicionais feitas para um caminho diferente dentro do período TTL do cache falham e retornam 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 explícito “Recurso” da API Gateway definido na política do IAM 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.

Resolução

Observação: talvez seja necessário modificar os trechos de código da função do autorizador do Lambda neste artigo para se adequar ao seu caso de uso.

Nas configurações de exemplo a seguir, as funções do Lambda extraem o valor deid da API do nome do recurso da Amazon (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 de id da 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âmetro de solicitação que retorna uma variável curinga “Recurso”

exports.handler = function(event, context, callback) {        
    console.log('Received event:', JSON.stringify(event, null, 2));

    // 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 Criação de funções do Lambda definidas como arquivos .zip. Além disso, consulte Editar código usando o editor de console no Guia do desenvolvedor do AWS Lambda.


Este artigo ajudou?


Precisa de ajuda com faturamento ou suporte técnico?