静的ウェブサイトおよびREST APIを認証してから利用できるようにしてみる

# 概要

本記事は [AWSでWebアプリを構築してみる](https://qiita.com/ti_and6id/items/9670ee60965a4d10bec7) シリーズの9回目の記事です。
前回の記事は [こちら](https://qiita.com/ti_and6id/items/2175e4421cbada9a242a)。

# 本記事で作成する構成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/50d2be0c-07ca-8be6-fea4-83d8db4ddf6f.png)

前回までの構成は、CloudFrontを経由して構築した静的ウェブサイトおよびREST APIに誰でもアクセスできる状態です。今回は、認証を設けて認証したユーザーのみが利用できるようにしてみます。

# 静的ウェブサイトおよびREST APIを認証してから利用できるようにしてみる

## 1. Cognito からユーザープールを作成する

1. マネジメントコンソールから Cognito を開き、「ユーザープールを作成」ボタンをクリック

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/4a1de5b1-00fb-c116-8382-4556c2e610f5.png)

1. サインインエクスペリエンスの設定はWebアプリの仕様に合わせて設定しますが、このWebアプリでは、プロバイダータイプは「Cognitoユーザープール」、Cognito ユーザープールのサインインオプションは「Eメール」のみを選択するとしました

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/2bdeec8d-5311-9540-011f-0c59757c661f.png)

1. セキュリティ要件の設定はWebアプリの仕様に合わせて設定しますが、このWebアプリでは、パスワードポリシーは「Cognitoのデフォルト」、多要素認証は「MFAなし」、ユーザーアカウントの復旧は無効化としました

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/1694b003-1052-93f0-a881-56b6af43bd04.png)

1. サインアップエクスペリエンスを設定はWebアプリの仕様に合わせて設定しますが、このWebアプリでは、自己登録を無効化としました

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/e335ea94-707f-77cf-5bb9-075eaa35a9d7.png)

1. メッセージ配信を設定はWebアプリの仕様に合わせて設定しますが、このWebアプリでは、Eメールプロバイダーを「CognitoでEメールを送信」、送信元Eメールアドレスはデフォルトの「no-reply@verficationemail.com」としました

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/2ad739ef-5186-a412-7cc9-3d1b39c87eaf.png)


1. アプリケーションを統合ではWebアプリの仕様に合わせて設定しますが、このWebアプリでは以下のように設定した

    * ユーザープール名：任意
    * ホストされた認証ページ：チェックあり
    * ドメイン：
      * ドメインタイプ：Cognitoドメインを使用する
      * Cognitoドメイン：任意
    * 最初のアプリケーションクライアント：
      * アプリケーションタイプ：パブリッククライアント
      * アプリケーションクライアント名：任意
      * 許可されているコールバックURL：CloudFrontのディストリビューションドメイン名を設定（例：https://xxxxx.cloudfront.net）

1. 以上の設定より、ユーザープールを作成する

## ユーザープールにユーザーを作成する

1. 作成したユーザープールを開き、ユーザータブから「ユーザーを作成」ボタンをクリックする

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/aedb5046-b7cb-c30a-22ae-e61b725ac25b.png)

1. 任意のメールアドレスとパスワードを設定して、ユーザーを作成する

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/a5d6e9ae-095c-d0fe-d0e2-99e20325bd0e.png)

1. この後で必要になるユーザープールID（作成したユーザープールの概要欄）とクライアントID（作成したユーザープールのアプリケーションの統合タブ内のアプリクライアントと分析欄）とCognitoドメイン（作成したユーザープールのアプリケーションの統合タブ内のドメイン欄）をメモしておく

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/f77f20d5-9ef1-f136-4020-25ba183f74b3.png)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/6973f5c4-8b40-69f0-75e9-2fa50b8070c6.png)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/0eb21d5f-0e94-778a-1ebc-48efbbbd16e4.png)

## Cognito ID プール (フェデレーティッド ID)を作成する

1. サイドメニューにあるIDプールから「IDプールを作成」ボタンをクリックする

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/cb875533-c9e5-2033-a7a9-f7b11844bdae.png)

1. IDプールの信頼を設定では、Webアプリの仕様に合わせて設定しますが、このWebアプリでは以下のように設定

    * ユーザーアクセス：認証されたアクセス
    * 認証されたIDソース：Amazon Cognitoユーザープール

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/cfa9c07c-30e3-f0ae-3600-e827e6fadfbd.png)

1. 許可を設定では、Webアプリの仕様に合わせて設定しますが、このWebアプリでは以下のように設定

    * IAMロール：新しいIAMロールを作成
    * IAMロール名：任意

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/b95a4c37-09d1-d208-6669-08318330a6cd.png)

1. IDプロバイダーを接続では、Webアプリの仕様に合わせて設定しますが、このWebアプリでは以下のように設定

    * ユーザープールID：さきほどメモしたユーザープールIDを選択
    * アプリクライアントID：さきほどメモしたアプリクライアントIDを選択

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/44f3fd34-2ac8-ac3d-d4d4-3abdc0870e1b.png)

1. プロパティを設定では、Webアプリの仕様に合わせて設定しますが、このWebアプリでは以下のように設定

    * Name：任意

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/5933859e-cf13-5734-fdfa-7f9b364eb4a9.png)

1. 以上の設定でIDプールを作成

1. 作成したユーザープールを開き、アプリケーションを統合タブからアプリクライアントと分析にある、作成したアプリクライアントのリンクをクリックし、「ホストされたUIを表示」をクリックし、作成したユーザーでサインインすると、認証してからWebアプリが表示されるようになるが、まだこの状態ではCloudFrontからは直接Webアプリが表示できてしまうため、次はLambda@Edgeを作成する

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/ce194d21-6c2d-c068-479f-9132099b2804.png)

## Lambda@Edge作成前の準備

[cognito-at-edge](https://github.com/awslabs/cognito-at-edge) に記載されている手順に従ってZIPファイルを作成します。

1. 以下のコマンドを実行し、index.jsにCognitoのユーザプールの情報を記述して簡単なハンドラを作成する

    ```
    npm init
    npm install cognito-at-edge
    touch index.js
    ```

    * index.js
    
        ```
        const { Authenticator } = require('cognito-at-edge');
    
        const authenticator = new Authenticator({
          // Replace these parameter values with those of your own environment
          region: 'us-east-1',
          userPoolId: 'メモしたユーザープールID',
          userPoolAppId: 'メモしたアプリクライアントID',
          userPoolDomain: 'メモしたドメイン',
          cookiePath: '/',
        });
    
        exports.handler = async (request) => authenticator.handle(request);
        ```

    * ZIPファイルを作成するが、1MBを超えると Lambda@Edge のパッケージのサイズ上限にひっかるため [こちら](https://zenn.dev/frommiddle1/articles/cloudfront-cognito) を参考に不要なファイルを削除してZIPを作成する

        ```
        find -name "*.map" | xargs rm -rf
        find -type d -name "test" | xargs rm -rf
        zip -r pkg.zip .
        ```

## Lambda@Edgeを作成する

1. マネジメントコンソールから Lambda を開き、リージョンを「バージニア北部」に変更する（Lambda@Edgeのデプロイはバージニア北部のみであるため）

1. 以下の内容で関数を作成する

    * 一から作成
    * 関数名：任意
    * ランタイム：今回は Node.js 20.x
    * アーキテクチャ：今回は x86_64
    * 実行ロール：AWS ポリシーテンプレートから新しいロールを作成
        * ロール名：任意
        * ポリシーテンプレート：基本的なLambda@Edgeのアクセス制限(CloudFrontトリガーの場合)

1. コードタブからZIPファイルをアップロードする

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/372b1aa6-7556-7815-fb75-fea256c58f03.png)

1. 設定タブのアクセス権限からCloudFrontトリガーを使用するためのLambda実行ロールの編集を行う

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/9c891d91-1f1d-c345-e0dd-100987820e8f.png)

1. アクションのLambda@Edgeへのデプロイをクリックし、以下の設定でデプロイする

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/0811fe3d-5eb6-dd0c-02ef-682856311bf3.png)

    * オプションの選択：新しい CloudFront トリガーの設定
    * ディストリビューション：作成済みのCloudFrontディストリビューション
    * CloudFrontイベント：ビューアーリクエスト（解説は [こちら](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/lambda-cloudfront-trigger-events.html) を参照）
    * ボディを含める：チェックあり
    * Lambda@Edgeへのデプロイを確認：チェックあり

1. マネジメントコンソールから CloudFront を開き、作成済みのCloudFrontのビヘイビアを開き、デフォルト (*)のビヘイビアを編集し、関数の関連付けの内容をメモしておく

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/b3bc7cdb-1db2-84bf-fd12-e7c1f90af997.png)

2. 同様に、/api/*のビヘイビアを編集し、メモして内容と同じく設定する


## 動作確認

以上の設定により、静的ウェブサイトおよびREST APIに対して認証しないと利用できなくなりました。

[静的ウェブサイトをCloudFront経由のアクセスしか許可しないようにしてみる](https://qiita.com/ti_and6id/items/c257f9d011f84b97ac3e) で作成したディストリビューションの一般タブを開き、「ディストリビューションドメイン名」をコピーしてブラウザで開き、アクセスしたときに以下のような認証を伴うようになります（シークレットウィンドウで開いてみてください）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/d46bbafe-79d0-575b-c4b6-5657d06ba36f.png)