Pourquoi ma ressource proxy API Gateway dotée d'un mécanisme d'autorisation Lambda dont la mise en cache est activée renvoie-t-elle des erreurs HTTP 403 « User is not authorized to access this resource » (L'utilisateur n'est pas autorisé à accéder à cette ressource) ?

Dernière mise à jour : 17-08-2022

Ma ressource proxy Amazon API Gateway dotée d'un mécanisme d'autorisation AWS Lambda dont la mise en cache est activée renvoie le message d'erreur HTTP 403 suivant : « User is not authorized to access this resource » (L'utilisateur n'est pas autorisé à accéder à cette ressource). D'où vient le problème et comment le résoudre ?

Brève description

Remarque : API Gateway peut renvoyer des erreurs 403User is not authorized to access this resource (L'utilisateur n'est pas autorisé à accéder à cette ressource) pour diverses raisons. Cet article concerne uniquement les erreurs 403 liées aux ressources proxy API Gateway dotées d'un mécanisme d'autorisation Lambda dont la mise en cache est activée. Pour en savoir plus sur le dépannage d'autres types d'erreurs 403, consultez la section Comment résoudre les erreurs HTTP 403 à partir de API Gateway ?

La sortie d'un mécanisme d'autorisation Lambda renvoie une politique AWS Identity and Access Management (IAM) à API Gateway. La politique IAM inclut un élément d'API « Resource » API Gateway explicite au format suivant :

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

Lorsque la mise en cache des autorisations est activée sur un mécanisme d'autorisation Lambda, cette politique IAM est mise en cache. La politique mise en cache est ensuite appliquée à toutes les demandes d'API supplémentaires effectuées au cours de la période time-to-live (TTL) spécifiée pour le cache.

Si l'API comprend une ressource proxy avec une variable de chemin gourmande{proxy+}, la première autorisation réussit. Toutes les demandes d'API supplémentaires effectuées pour un chemin différent au cours de la période TTL du cache échouent et renvoient l'erreur suivante :

« message » : « User is not authorized to access this resource » (L'utilisateur n'est pas autorisé à accéder à cette ressource)

Les demandes supplémentaires échouent, car les chemins ne correspondent pas à l'élément d'API « Resource » API Gateway explicite défini dans la politique IAM mise en cache.

Pour résoudre le problème, vous pouvez modifier le code de la fonction du mécanisme d'autorisation Lambda pour renvoyer une ressource générique (*/*) dans la sortie à la place. Pour plus d'informations, consultez la section Ressources et conditions pour les actions Lambda.

Remarque : pour activer la mise en cache du mécanisme d'autorisation, celui-ci doit renvoyer une stratégie applicable à toutes les méthodes d'une API Gateway. Le code de la fonction d'autorisation Lambda doit renvoyer une ressource générique (*/*) dans la sortie pour autoriser toutes les ressources. La stratégie de cache s'attend à ce que le même chemin de ressources soit mis en cache, sauf si vous avez effectué deux fois la même demande sur le même chemin de ressource.

Solution

Remarque : vous devrez peut-être modifier les exemples d'extraits de code de fonction de mécanisme d'autorisation Lambda de cet article pour les adapter à votre cas d'utilisation.

Dans les exemples de configuration suivants, les fonctions Lambda extraient la valeur id de l'API de l'Amazon Resource Name (ARN) de la méthode (« event.methodArn »). Ensuite, les fonctions définissent une variable générique « Resource » en combinant les chemins de l'ARN de la méthode avec la valeur id de l'API et un caractère générique (*/*).

Exemple de code de fonction de mécanisme d'autorisation Lambda basé sur un jeton qui renvoie une variable générique « Resource »

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

Exemple de code de fonction de mécanisme d'autorisation Lambda basé sur un paramètre de demande qui renvoie une variable générique « Resource »

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

Pour plus d'informations sur la façon de modifier le code de fonction Lambda, consultez la section Création de fonctions Lambda définies en tant qu'archives de fichiers .zip.


Cet article vous a-t-il été utile ?


Avez-vous besoin d'aide pour une question technique ou de facturation ?