【AWS】API Gateway Lambdaオーソライザー「User is not authorized to access this resource」エラーの原因と対応

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312085/322187ad-6ff5-1eb3-5044-8c2e12891a23.png)

上記のようなAPIGatewayで構築したAPIにLambdaオーソライザーを適用した際に、正しい認証情報、正しいAWSリソースへのアクセス権限を設定しているにも関わらず「**User is not authorized to access this resource**」エラーが発生することがあります。
本記事ではその原因と対応を記載します。

## 原因
**Lambdaオーソライザーから返却される認可情報のキャッシング**が原因です。

以下のようなアクセスポリシーを返却するコードを記述した際に発生します。

```node.js
  ...
  statementOne.Action = 'execute-api:Invoke'; 
  statementOne.Effect = 'Allow';
  // ↓ 以下のアクセス承認するリソースを指定するコードが原因
  statementOne.Resource = event.methodArn;
  ...
```

ポイントは`event.methodArn`です。
ここでは呼び出しされたAPIのみのArnが含まれています。
`例： arn:aws:execute-api:ap-northeast-1:xxxxxxxxxxx:xxxxxxxxx/stg/GET/users/43/info`

このArnへのアクセス権限を許可したポリシーを返却すると、初回のアクセスはうまくいきます。

![2 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312085/f67b2167-fb39-77c4-cbb0-8756b1c2e0c9.png)


しかし**同じLambdaオーソライザーが適用されている他のAPIを呼び出した際に、上記のArnが含まれたポリシーがキャッシュとして返却**されてしまい、**初回アクセスとは別の認可されていないリソースにアクセスすると「User is not authorized to access this resource」エラーが発生**します。

![3 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312085/99ffc2a5-caa8-87b3-af31-fb5504a40351.png)

## 対応
2パターンの対応方法があります。

### パターン１Lambdaオーソライザーの認可のキャッシュのTTL値を0に設定

API Gatewayのオーソライザーから対象のオーソライザーを選択して、
<img width="471" alt="スクリーンショット 2020-08-06 17.43.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312085/46591280-c5b8-e540-79b1-1b41633ac4ae.png">
TTLを0に変更すると、毎回認証を実行するため`event.methodArn`をアクセス承認リソースとして返却しても、問題なく認可が通ります。
ただ毎回認証処理が走るため、**パフォーマンス的には優れません**。
その点が気になる場合は、後述のパターン2を利用することを推奨します。

**※ AWS CloudFormation, AWS SAMを利用する場合の注意点**
CloudFormation、SAMを利用してLambdaオーソライザーを設定した場合、デフォルトでは認可のキャッシュのTTL値は300秒です。
認可キャッシュのTTL値を変更せずにデプロイしたオーソライザーの設定値をマネジメントコンソールから確認すると...

<img width="450" alt="スクリーンショット 2020-08-06 17.56.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312085/45352f2d-1fcc-4ce6-0b9e-b30d7a92582a.png">

こんな感じで`undefined`となっており、キャッシュが無効になっているように見えます。
しかし**実際には300秒のキャッシュ設定**が有効になっています。
(これに1時間くらいハメられました...)

TTLを0に設定する際には、`ReauthorizeEvery`を0に設定しましょう。

```yaml:template.yaml
  TestApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prd
      Auth:
        AddDefaultAuthorizerToCorsPreflight: false
        DefaultAuthorizer: MyLambdaCustomAuthorizer
        Authorizers:
          MyLambdaCustomAuthorizer:
            FunctionPayloadType: REQUEST
            FunctionArn:
              Fn::GetAtt:
              - RestApiAuthFunction
              - Arn
            Identity:
              QueryStrings:
              - authToken
              ReauthorizeEvery: 0
```


### パターン2 Lambdaオーソライザー側で返却するリソースポリシーを変更
Lambdaオーソライザー側で`event.methodArn`を指定するのではなく、ワイルドカードを指定して対応する方法です。

```node.js
  ...
  statementOne.Action = 'execute-api:Invoke'; 
  statementOne.Effect = 'Allow';
  // ↓ ワイルドカード指定に変更
  statementOne.Resource = 'arn:aws:execute-api:*';
  ...
```

上記例では、全Lambdaの実行権限を返却しています。
こうすることでポリシーのキャッシュが行われていても、ワイルドカードで許可されたリソースは問題なくコールすることが可能です。
どこまでをワイルドカード指定にするかは、自身のセキュリティポリシーと相談してみてください。

キャッシュと併用することができるため、パターン１よりもパフォーマンス的に優れていますが、ワイルドカード指定になる分、セキュリティとトレードオフです。

## まとめ
- １つのオーソライザーを複数のリソースで共有した際に、オーソライザーの認可キャッシュが有効になっていると「User is not authorized to access this resource」が発生する。
- オーソライザーの認可情報のキャッシュが原因であるため、TTLを0にするか認可リソースを拡大することで対応可能

記載情報に誤りがあったらご指摘いただけると助かります。

## 参考リンク
- [AWS公式 : API Gateway コンソールを使用して Lambda オーソライザーを設定する](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/configure-api-gateway-lambda-authorization-with-console.html)
- [stackoverflow : AWS API Gateway custom Authorizer strange showing error]
(https://stackoverflow.com/questions/50331588/aws-api-gateway-custom-authorizer-strange-showing-error)