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”?

6 minuto de leitura
0

Meu recurso de proxy do Amazon API Gateway com um AWS Lambda Authorizer 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>/<http-method>/[<resource-path-name>/[<child-resources-path>]"

Quando o cache de autorização é ativado em um autorizador do Lambda, a política do IAM retornada é armazenada em cache. A política do IAM 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.

Observação: para ativar o cache do autorizador, seu autorizador deve retornar uma política que seja 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 cache espera o mesmo caminho de recurso 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 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 de id do API Gateway 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) {        
   
    // 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 Implantação de funções do Lambda definidas como arquivos .zip.


Informações relacionadas

Editar código usando o editor do console