【API Gateway と Cognito の連携】API Gateway の Authorizer を設定してみる

# Authorizer が必要になる場面とは
　ある特定のユーザーにだけ、特定のAPIへのアクセスを許可したい場合などに用いられます。例えば、サインインしているユーザーは API1 と API2 が叩けるが、サインインしていない全てのユーザーは API 1 のみが叩けるようにしたい場合などです。具体的アクションとしては、Twitter のようにタイムラインを見る（GET）ことや、投稿（POST）があげられます。

# どのようにユーザーを判別しているか
　Cognito に対しサインインアクションを行うとID トークンが返されます。ID トークンはブラウザのローカルストレージに格納されています。デベロッパーツールを開くと、図のように確認することができます（Safari での確認）。

![safari_idtoken.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431877/d6fde8a7-731e-84a1-4e3e-c93f9bd3aefc.png)

　ID トークンはその保有者が認証されているかどうかを保証する、いわば身分証のようなものです。API Gateway 側で、身分証を持っているか、または身分証が有効なものかを判断することができれば、サインインしているユーザーとしていないユーザーで API へのアクセスを制御できます。
　今回の記事では既に Cognito に登録されたユーザーが存在しているとして進めます。

# API Gateway で Authorizer の設定を行う
### REST API の作成
　Example API として `PetStore` という API が用意されています。今回はこれをインポートしてさくっと API を作成してしまいます。
　`Create API` > `Import (REST API)` > `Example API にチェック` > `Import`
の順に進めると、`PetStore` という名前の API Gateway が作成されます。

### PetStore の挙動の確認
　実際に API をデプロイして挙動を確認してみましょう。
　`Action` > `API のデプロイ` > `デプロイされるステージで [新しいステージ] を選択` > `ステージ名に prod と記入` > `デプロイ`
の順に進めるとデプロイされます。
　遷移後の画面ではデプロイされた API の URL が確認できます。URL をコピーし、末尾に `/pets` を加えてアクセスしてみましょう。
例）`https://XXXXX.execute-api.ap-northeast-1.amazonaws.com/prod/pets`
すると図のようなリスト形式の文字列が返ってくることが確認できます。

```
[
  {
    "id": 1,
    "type": "dog",
    "price": 249.99
  },
  {
    "id": 2,
    "type": "cat",
    "price": 124.99
  },
  {
    "id": 3,
    "type": "fish",
    "price": 0.99
  }
]
```

### Authorizer の設定
　左メニューから`オーソライザー`を選択し、`新しいオーソライザーの作成` を押します。名前、タイプ、Cognito ユーザープール、トークンのソースの4つのパラメータを設定する必要があります。ここでは以下のように設定します。

![authorizer_setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431877/cfbc7472-be43-28a1-19ee-2e13d5a2de0c.png)

　/pets の GET メソッドに対してオーソライザーを設定します。
　`リソース` > `/pets の GET` > `メソッドリクエスト`
の順に進みます。
　認可のペンアイコンをクリックし、先ほど作成した `authorizer-test` を選択し、チェックマークアイコンを押します。以下の図のように設定されれば OK です。API をデプロイして設定を反映させましょう。
※ `authorizer-test` が選択肢に現れない場合は画面をリロードしてみてください。

![authorizer_setting_in_get_method.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431877/c77a264b-e288-35ac-22ce-42af79a14d54.png)

　/pets に対応するメソッドにオーソライザーを設定したので、`https://XXXXX.execute-api.ap-northeast-1.amazonaws.com/prod/pets` にアクセスすると `{"message":"Unauthorized"}` が返されることが確認できます。
　`{"message":"Unauthorized"}` となったのは、GET リクエストのヘッダーに Authorization のフィールドが設定されていないので想定通りの挙動になります。

[^1]: 固有の値となります。検証するユーザーが登録されているユーザープールを選択してください。

# Authorizer の確認
❗️注意❗️
ヘッダーに Authorization フィールドを挿入（上書き？）すると、他のアプリと干渉が生じるようです。私は Youtube が勝手にログアウトされ、Twitter が見れなくなりました。その場合は焦らず ModHeader から設定を削除すれば大丈夫です。

　HTTP ヘッダーに Authorization フィールドを設定し、API にアクセスできるか確認してみます。ヘッダーにフィールドを追加するツールとして、[ModHeader](https://chrome.google.com/webstore/detail/modheader/idgpnmonknjnojddfkpgkljpfnnfcklj) を使用します。
　`https://XXXXX.execute-api.ap-northeast-1.amazonaws.com/prod/pets` にアクセスすると`{"message":"Unauthorized"}` が表示されます。この状態で ModHeader を開き、フィールド名 `Authorization` と ID トークン `ZZZZZ...` を記入します（ID トークン `ZZZZZ...` がわからない場合は `AWS CLI で ID トークンを取得` を参考にしてみてください）。

![Screen Shot 2021-12-01 at 15.20.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431877/c94bb317-7dc0-7a25-2a3a-65f153b9fc81.png)

　画面をリロードし、ペットの情報が返ってくれば成功です。これで認証されたユーザー（有効な ID トークンを保有しているユーザー）がアクセスできる API を設定することができました。

### AWS CLI で ID トークンを取得
　Webアプリ等で認証機能が設定済みであれば、ID トークンはブラウザのローカルストレージから確認することができるかと思います。私は AWS CLI から ID トークンの情報を取得して、ヘッダーに追加して検証を行いました。そのコマンドも記しておきます。

```
aws cognito-idp admin-initiate-auth --user-pool-id ap-northeast-1_xxxxx --client-id yyyyy --auth-flow ADMIN_NO_SRP_AUTH --auth-parameters USERNAME=hogehoge,PASSWORD=fugafuga
```

ap-northeast-1_xxxxx: ユーザープールのID
yyyyy: アプリクライアントのID
hogehoge: ユーザー名
fugafuga: hogehogeのパスワード

# 参考資料
[Cognitoで認証されたユーザーだけがAPI Gatewayを呼び出せるオーソライザーを使ってみた](https://dev.classmethod.jp/articles/api-gateway-cognito-authorizer/)