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

所要時間4分
0

キャッシュが有効になっている 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 から 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>/<http-method>/[<resource-path-name>/[<child-resources-path>]"

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

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

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

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

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

: オーソライザーのキャッシュを有効にするには、オーソライザーが API Gateway のすべてのメソッドに適用可能なポリシーを返す必要があります。すべてのリソースを許可するには、Lambda Authorizer 関数のコードが出力にワイルドカード (*/*) リソースを返す必要があります。キャッシュポリシーは、同じ resource-path に対して同じリクエストを 2 回実行しない限り、同じリソースパスがキャッシュされることを想定しています。

解決方法

注意: この記事の 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) {        
   
    // 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 関数のデプロイ」を参照してください。


関連情報

コンソールエディタを使用してコードを編集する

コメントはありません

関連するコンテンツ