Lambdaオーソライザーで独自認証処理を実装する

# この記事について

API Gatewayのアクセス制御の仕組みの1つに「Lambdaオーソライザー」という機能があります。
今回はLambdaオーソライザーを使って、
クエリパラメータ「type」の値によって後継のAPIを実行するかどうかを判断する使い方をしてみたいと思います。
Lambdaオーソライザーを作成して動きを確認することを目的としているので、
LambdaオーソライザーやAPI Gatewayの仕様に関しては説明をしません。
AWS公式HPで補足をお願いします。
[API Gateway Lambda オーソライザーを使用する](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html#api-gateway-lambda-authorizer-flow)

# 対象者
* Lambdaオーソライザーを作成したことがなく、サクッと挙動を確認してみたい。


# API Gatewayの準備
サンプルAPIを利用して、REST APIを作成します。
![スクリーンショット 2023-10-04 15.11.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77473/cdd869b5-b1d6-3d99-c3f5-6006bf04ed93.png)

# Lambdaの準備
オーソライザーで実行するLambdaを準備します。
今回はこんな感じのLabmdaを用意したいと思います。

* typeがuserなら後継のAPIを実行
* typeがuser以外ならエラーを返す

<details><summary>index.js</summary>

```rb
exports.handler = async (event) => {
    const queryParameters = event.queryStringParameters;

    if (queryParameters && queryParameters.type === 'user') {
        return generatePolicy('user', 'Allow', event.methodArn);
    }
    return generatePolicy('user', 'Deny', event.methodArn);
};

function generatePolicy(principalId, effect, resource) {
    const policyDocument = {
        Version: '2012-10-17',
        Statement: [{
            Action: 'execute-api:Invoke',
            Effect: effect,
            Resource: resource
        }]
    };

    return {
        principalId: principalId,
        policyDocument: policyDocument
    };
}

```
</details>


# Lambdaオーソライザーの作成
API Gateway左のナビゲーションから「オーソライザーを選択」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77473/e0609353-42a2-bc53-318d-b32e534bf61f.png)

下記設定でオーソライザーを作成します。
* オーソライザー名：任意の名前
* オーソライザーのタイプ：Lambda
* Lambda関数：事前に作成したLambda
* Lambday呼び出しロール：今回は未指定
* Lambdaイベントペイロード:リクエスト＞IDソースタイプ：クエリ文字列, 値：type
* 認可のキャッシュ：無効

## LambdaオーソライザーをAPIに紐付ける

**LambdaオーソライザーはAPI Gaetwayのメソッドに紐づく**ので、
紐付けたいメソッドにLambdaオーソライザーを紐付けます。

今回は/pets　GETメソッドに紐付けます。
メソッドリクエストの編集で、認可タイプに作成したLambdaオーソライザーを選択してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77473/60cbd6f8-4023-c7de-db74-1563b3b9608b.png)



# 動作確認
APIをデプロイして、クエリパラメータtypeを付けてリクエストしてください。
（/pets?type=user）

typeがuser だとpets一覧がレスポンスされますが、guestなど他の値だとHTTPStatus４０３がレスポンスされます。


## API GatewayのログをCloudWatchに出力して、後継のAPIが実行されていないことを確認する

### IAM Role作成
AmazonAPIGatewayPushToCloudWatchLogs

### API GatewayにRoleを割り当て
左のナビゲーションから「設定」を選択。
ログ記録で「CloudWatch ログのロール ARN」に作成したRoleのArnを指定する。

### ログの有効化
デプロイしたステージの「ログとトレース」で下記設定を行う。
CloudWatchログ：リクエストとレスポンスの完全なログ


### 確認

１. クエリパラメータtype=guestでリクエスト
.CloudWatchLogs「　API-Gateway-Execution-Logs_{random}/dev」というロググループが作成されているので、
内容を確認する

>The client is not authorized to perform this operation.

２. クエリパラメータtype=userでリクエスト
「　API-Gateway-Execution-Logs_{random}/dev」に下記ログが出力され、後継のAPIの実行ログが続いて出力されていることが確認できる
>Successfully completed authorizer execution


以上です。
本来の使い方ではないですが、Lambdaオーソライザーを使ったAPI GWの動きを確かめたい方の参考になれば幸いです。

### 補足

#### ペイロードについて
今回、オーソライザー作成時に下記設定を行いました。
* Lambdaイベントペイロード:リクエスト＞IDソースタイプ：クエリ文字列, 値：type

そもそも**クエリ文字列にtypeがない場合は、オーソライザーとして指定したLambdaは実行されません。**
ペイロードではtypeを指定していますが、typeを使わない検証を行うことも可能です。

#### APIキーと組み合わせた場合の実行順序
ちなみ APIキーと組み合わせた場合は、
オーソライザーでの認可が通った後にAPIキーがチェックされるようです。
https://qiita.com/yaguchii/items/d38d3203b611c6a15066#:~:text=Lambda%E3%82%AA%E3%83%BC%E3%82%BD%E3%83%A9%E3%82%A4%E3%82%B6%E3%83%BC%E3%81%A8API%E3%82%AD%E3%83%BC%E8%AA%8D%E8%A8%BC%E3%82%92%E5%90%8C%E6%99%82%E3%81%AB%E5%88%A9%E7%94%A8,%E8%AA%8D%E8%A8%BC%E3%81%AB%E3%81%AF%E3%81%84%E3%81%8D%E3%81%BE%E3%81%9B%E3%82%93%E3%80%82

今回はAPIキーを使っていませんが、ログを確認すると
「Successfully completed authorizer execution」の後に下記ログが出力されていることからも確認ができます。
>API Key  authorized because method 'GET /pets' does not require API Key. Request will not contribute to throttle or quota limits