キャッシュが有効になっている Lambda オーソライザーを使用する API Gateway プロキシリソースが、HTTP 403「User is not authorized to access this resource (ユーザーはこのリソースへのアクセスを許可されていません)」というエラーを返すのはなぜですか。

最終更新日時: 2022 年 2 月 4 日

キャッシュが有効になっている AWS Lambda オーソライザーを使用する Amazon API Gateway プロキシリソースが、「User is not authorized to access this resource (ユーザーはこのリソースへのアクセスを許可されていません)」という HTTP 403 エラーメッセージを返します。なぜこのような問題が発生するのですか。これを解決するにはどうすればよいですか。

簡単な説明

注: API Gateway は、さまざまな理由から「User is not authorized to access this resource (ユーザーはこのリソースへのアクセスを許可されていません)」という 403 エラーを返すことがあります。この記事では、キャッシュのみアクティブになっている Lambda オーソライザーを使用した API Gateway プロキシリソースに関連する 403 エラーを解決する方法について説明します。他の種類の 403 エラーのトラブルシューティングについては、「API Gateway REST API から HTTP 403 エラーをトラブルシューティングする方法を教えてください。」を参照してください。

Lambda オーソライザーの出力は、AWS Identity and Access Management (IAM) ポリシーを API Gateway に返します。IAM ポリシーには、次の形式の明示的な API Gateway API「Resource」要素が含まれています。

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

[Authorization Caching] (認可のキャッシュ) が Lambda オーソライザーで有効になっている場合、この IAM ポリシーはキャッシュされます。キャッシュされたポリシーは、キャッシュの指定された有効期限 (TTL) 内に実行された追加の API リクエストに適用されます。

API に greedy パス変数 {proxy+} を持つプロキシリソースがある場合、最初の認可は成功します。キャッシュの TTL 期限内に別のパスに対して行われた追加の API リクエストはすべて失敗し、次のエラーが返されます。

"message": "User is not authorized to access this resource"

キャッシュされた IAM ポリシーで定義されている明示的な API Gateway API「Resource」要素にパスが一致しないため、追加のリクエストは失敗します。

この問題を解決するに、代わりにワイルドカード (*/*) リソースを出力として返すように、Lambda オーソライザー関数のコードを変更できます。 詳細については、「Lambda アクションのリソースと条件」を参照してください。

解決方法

注: この記事の Lambda オーソライザー関数のサンプルコードスニペットは、ユースケースに合わせて変更する必要がある場合があります。

以下のセットアップ例では、Lambda 関数がメソッドの Amazon リソースネーム (ARN) (「event.methodArn」) から API の idを抽出します。次に、メソッド ARN のパスと API の id 値およびワイルドカード (*/*) を組み合わせて、ワイルドカードの「Resource」 変数を定義します。

ワイルドカード「Resource」変数を返すトークンベースの Lambda オーソライザー関数コードの例

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

ワイルドカード「Resource」変数を返すリクエストパラメータベースの Lambda オーソライザー関数コードの例

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

Lambda 関数コードの編集方法の詳細については、「.zip ファイルアーカイブとして定義された Lambda 関数の作成」を参照してください。また、AWS Lambda デベロッパーガイドの「コンソールエディタを使用したコードの編集」も参照してください。


この記事はお役に立ちましたか?


請求に関するサポートまたは技術サポートが必要ですか?