¿Por qué mi recurso de proxy de API Gateway con un autorizador de Lambda que tiene activado el almacenamiento en caché devuelve errores HTTP 403 «El usuario no está autorizado a acceder a este recurso»?

6 minutos de lectura
0

Mi recurso de proxy de Amazon API Gateway con un autorizador de AWS Lambda que tiene activado el almacenamiento en caché devuelve el siguiente mensaje de error HTTP 403: «El usuario no está autorizado a acceder a este recurso».

Descripción breve

Nota: API Gateway puede devolver errores 403 El usuario no está autorizado a acceder a este recurso por diversos motivos. Este artículo aborda los errores 403 relacionados con los recursos de proxy de API Gateway con un autorizador de Lambda que solo tiene activado el almacenamiento en caché. Para obtener información sobre cómo solucionar otros tipos de errores 403, consulta ¿Cómo puedo solucionar los errores HTTP 403 desde API Gateway?

El resultado de un autorizador de Lambda devuelve una política de AWS Identity and Access Management (IAM) a API Gateway. La política de IAM incluye un elemento «recurso» explícito de la API de API Gateway que tiene el siguiente formato:

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

Cuando se activa el almacenamiento en caché de autorizaciones en un autorizador de Lambda, la política de IAM devuelta se almacena en caché. La política de IAM almacenada en caché se aplica entonces a cualquier solicitud de API adicional que se realice dentro del período de vida (TTL) especificado por la caché.

Si la API tiene un recurso de proxy con una variable de ruta expansiva de {proxy+}, la primera autorización se realiza correctamente. Todas las solicitudes de API adicionales realizadas a una ruta diferente dentro del período TTL de la caché fallan y devuelven el siguiente error:

"message": «El usuario no está autorizado a acceder a este recurso»

Las solicitudes adicionales fallan porque las rutas no coinciden con el elemento «recurso» explícito de la API de API Gateway definido en la política de IAM almacenada en caché.

Para resolver el problema, puedes modificar el código de la función del autorizador de Lambda para que, en su lugar, devuelva un recurso comodín (*/*) en el resultado. Para obtener más información, consulta Recursos y condiciones para las acciones de Lambda.

Nota: Para activar el almacenamiento en caché del autorizador, este debe devolver una política que se aplique a todos los métodos de API Gateway. El código de la función del autorizador de Lambda debe devolver un recurso comodín (*/*) en el resultado para permitir todos los recursos. La política de caché espera que se almacene en caché la misma ruta de recursos, a menos que hayas realizado la misma solicitud dos veces en la misma ruta de recursos.

Resolución

Nota: Modifica los fragmentos de código de la función del autorizador de Lambda de ejemplo de este artículo para adaptarlos a tu caso de uso.

En las siguientes configuraciones de ejemplo, las funciones de Lambda extraen el valor de ID de API Gateway del nombre de recurso de Amazon (ARN) («event.methodArn»). A continuación, las funciones definen una variable de «Recurso» comodín combinando las rutas del ARN del método con el valor de ID de la API y un comodín (*/*).

Ejemplo de código de función del autorizador de Lambda basado en un token que devuelve una variable de «Recurso» comodín

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;
}

Ejemplo de código de función del autorizador de Lambda basado en parámetros de solicitud que devuelve una variable de «Recurso» comodín

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 obtener más información sobre cómo editar el código de las funciones de Lambda, consulta Implementación de funciones de Lambda definidas como archivos de archivos.zip.

Información relacionada

Prueba de las funciones de Lambda en la consola