🌟

Cognito認証を導入した振り返り

2023/04/13に公開

背景/目的

S3に置いているコンテンツを下記の構成で作成し静的WEBサイトを展開していたのですが、認証を導入したいと思い、今回Cognitoを導入しました。

Cognito認証の紹介記事は多くあるのですが、マネコンのUIが変わっていたり(特にCognito)、私がAWS初心者ということもあり躓いた部分が多々あったので備忘録も兼ねて手順を記載します。
有識者が作ったCDKのコードを使うと一瞬でデプロイできますが、CDKコードを読むのがつらいので今回はマネジメントコンソールでの実施とします。S3バケットの作成から記載しますので、不要な部分は読み飛ばしてください。
また、各サービスについてはGPT4に聞いた内容をコピペしておきます。

注意事項

  • 操作はすべてマネジメントコンソールで行います。
  • 本記事はCognitoがメインなので、基本的な操作(例:S3バケットの作成方法)は可能な範囲で記載します。
  • 各種設定は重要な部分をメインに記載します。
  • 雰囲気で設定している箇所も多いのでご自身の実現したいシステムに合わせて作成してください。

ゴール

本記事の目標は下記の構成です。

ですが、いっぺんに作成しても良く分からないと思うので、段階的に作成して動作確認しながら勧めていきたいと思います。

手順

CloudFront + S3

S3:(GPT4より)

  • スケーラビリティ:S3は、ユーザーが必要なだけデータを格納できるように設計されており、数バイトから数ペタバイトまでのデータを保存できます。
  • 耐久性と可用性:S3は、データを複数の物理的データセンターに自動的に複製することで、高い耐久性(99.999999999%)を提供します。また、99.99%のサービス可用性を提供しています。
  • セキュリティ:S3は、データの暗号化、アクセス管理、監査機能を提供し、データのプライバシーとセキュリティを確保します。
  • 柔軟性:S3は、異なるストレージクラス(S3 One Zone-Infrequent Access、S3 Intelligent-Tiering、S3 Glacierなど)を提供しており、データのアクセス頻度や耐久性要件に応じて、コストを最適化できます。

CloudFront:(GPT4より)

  • パフォーマンスの向上:CloudFrontは、AWSのグローバルなエッジロケーションネットワークを利用して、コンテンツをユーザーに近いエッジサーバーにキャッシュすることで、低遅延で高速なコンテンツ配信を実現します。
  • セキュリティ:CloudFrontは、TLS/SSLを用いたデータの暗号化や、AWS WAF(Web Application Firewall)との統合によるアプリケーション保護、アクセス制限機能(IPアドレス、地理的制限など)を提供し、セキュアなコンテンツ配信をサポートします。
  • スケーラビリティ:トラフィックの増減に対して自動的にスケールし、大規模なトラフィックにも対応できる設計がされています。
  • コスト効率:CloudFrontは、データ転送料金、リクエスト数に応じた従量課金制で、コスト効率の高いコンテンツ配信が可能です。また、長期間の利用や大容量のデータ転送を行う場合には、より割引率の高い料金プランも提供されています。
  • AWSとの統合:CloudFrontは、他のAWSサービス(Amazon S3、Amazon EC2、Elastic Load Balancing、AWS Lambda@Edgeなど)とシームレスに統合されており、簡単にAWS環境でのコンテンツ配信が行えます。

S3

  1. マネジメントコンソールからS3を表示
  2. バケットの作成
    • 任意のバケット名
    • 任意のリージョン
    • ACL無効
    • このバケットのブロックパブリックアクセス設定:パブリックアクセスをすべてブロック
  3. index.htmlをアップロード。中身は何でも良いです。

CloudFront + S3連携

  1. マネジメントコンソールからCloudFrontを表示
  2. ディストリビューションを作成
    • オリジンドメイン:上記で作成したS3バケット
    • S3バケットアクセス:Origin access control settings (OAC)
    • コントロール設定を作成:
      • オリジンタイプ:S3
    • デフォルトルートオブジェクト:index.html
  3. 作成が完了すると下記のメッセージが表示されるので、ポリシーをコピーしておく。
  4. 先程作成したS3バケットでバケットポリシーにコピーしたポリシーを設定。

動作確認

  1. CloudFrontのディストリビューションのページを表示し、ディストリビューションドメインをコピー
  2. 任意のブラウザのURLの部分に貼り付けて表示。(まだCognito連携してません😂)

以上でCloudFrontとS3の連携が完了したので、Cognito連携を行っていきます。

Cognito + Lambda@Edge + CloudFront + S3

Cognito:(GPT4より)

  • ユーザープール:ユーザープールは、アプリケーションのユーザーを管理するためのディレクトリです。Cognitoは、ユーザーのサインアップやサインイン、パスワードリセット、マルチファクタ認証(MFA)などの機能を提供します。また、ソーシャルログイン(Facebook, Googleなど)や企業向けのシングルサインオン(SSO)など、他の認証プロバイダーとも連携できます。
  • アイデンティティプール:アイデンティティプールは、認証されたユーザーや未認証のゲストユーザーに対して、一時的なAWSアクセス許可を付与する機能です。これにより、ユーザーは直接AWSリソース(例:Amazon S3やAmazon DynamoDB)にアクセスできるようになり、バックエンドサーバーを介さずにアプリケーションの機能を利用することが可能です。
  • セキュリティ:Cognitoは、セキュアなパスワードポリシー、マルチファクタ認証、暗号化されたデータストレージなどを提供し、ユーザーデータのセキュリティを確保します。
  • スケーラビリティ:Cognitoは、数人から数百万人のユーザーまで、自動的にスケールすることができます。
  • 簡単な統合:Cognitoは、AWS SDKやCognito SDKを利用して、アプリケーションに簡単に統合することができます。

Lambda@Edge:(GPT4より)

  • 低遅延:Lambda関数がエッジロケーションで実行されるため、低遅延でリクエストやレスポンスを処理できます。
  • グローバルスケール:CloudFrontのエッジネットワークを活用して、世界中のユーザーに対応できます。
  • サーバーレス:Lambda@Edgeはサーバーレスアーキテクチャを採用しており、インフラストラクチャ管理やキャパシティプロビジョニングの手間が不要です。

Cognito ユーザープール

  1. マネジメントコンソールからCognitoを表示
  2. ユーザープールを作成
  3. サインインエクスペリエンスを設定
    • Cognitoユーザプールのサインインオプション:Eメール
  4. セキュリティ要件を設定
    • 多要素認証:MFAなし(今回は認証の手間を省きたいため)
  5. サインアップエクスペリエンスを設定
    • 変更なし
  6. メッセージ配信を設定
    • E メールプロバイダー:CognitoでEメールを送信
    • 送信元Eメールアドレス:no-reply@verficationemail.com
  7. アプリケーションを統合
    • ユーザープール名:任意
    • ホストされた認証ページ:チェックあり
    • ドメイン:
      • ドメインタイプ:Cognitoドメインを使用する
      • Cognitoドメイン:任意
    • 最初のアプリケーションクライアント:
      • アプリケーションタイプ:パブリッククライアント
      • アプリケーションクライアント名:任意
      • 許可されているコールバック URL:CloudFrontのディストリビューションドメイン名を設定(例:https://xxxxx.cloudfront.net
        • 認証後に遷移させたいページのURL
  8. ユーザープールを作成

一旦上記の設定で良いと思いますが、自分の用途に合わせて設定を変えてください。私の場合は新規ユーザの作成を制限したいので、後でセルフサービスのサインアップを無効にしたり、MFA認証を導入したりしています。

Cognito ID プール (フェデレーティッド ID)

  1. IDプール作成に必要な値をメモする。(度々必要になります)
    • ユーザープールID
    • クライアントID (アプリケーションの統合タブ)
  2. 左メニューの「フェデレーティッド ID」を選択
  3. 新しいIDプールの作成
    • IDプール名:任意
    • 認証プロバイダー:Cognito
      • ユーザプールID:メモした値
      • アプリクライアントID:メモした値
  4. 許可

ユーザー作成

  1. ユーザープールのアプリケーションの統合を表示。
  2. 画面下部のアプリクライアントと分析のアプリケーションクライアント名を選択。
  3. ホストされたUI > ホストされたUIを表示 を選択
  4. アカウント登録(Sign up)
  5. メールでVerification codeが届くので入力する。するとコールバックURLに指定したページに遷移します。
  6. ユーザープール > ユーザー に先程登録したメールアドレスでユーザー登録ができている事がわかります。

以上でCognitoのユーザ登録が完了し、ログイン処理後に期待したページが表示されるところまで出来ました。
しかし、今の設定では、Cognitoのログインの有無に関係なくページが見えてしまうので、認証情報を検証するLambda@EdgeをデプロイしCloudFrontに紐付けます。

Lambda@Edge

準備(ローカル環境で実施)

awslabsの下記のリポジトリを参考に準備します。
https://github.com/awslabs/cognito-at-edge
npmのインストール手順は割愛します。

コードを見れば分かるのですが、Lambda@EdgeへプールID等の値を渡す必要があります。しかし、Lambda@Edgeでは通常Lambdaで使用できる環境変数を設定できません。

したがって、2パターンが考えられますが今回は楽な方法1の手順を記載します。

  • 方法1:コードにプールID等の値をハードコーディングする
  • 方法2:Systems Managerのパラメータストアを作成し、Lambda@Edgeから参照する
    • 大雑把な手順(方法1との差分)
      1. System Managerのパラメータストアに登録
      2. Lambdaの実行ロールにパラメータストアを参照するためのポリシーを追加
      3. パラメータストアから値を取り出すようLambdaのコードを変更
方法1
  1. index.js作成・cognito-at-edgeインストール
$ cd [任意のディレクトリ]
$ mkdir lambda_edge
$ cd lambda_edge
$ npm install cognito-at-edge
$ touch index.js
  1. 任意のテキストエディタでindex.jsを編集する。(下記は上記リポジトリのREADMEから引用)
const { Authenticator } = require('cognito-at-edge');

const authenticator = new Authenticator({
  // Replace these parameter values with those of your own environment
  region: 'us-east-1', // user pool region
  userPoolId: 'us-east-1_tyo1a1FHH', // user pool ID
  userPoolAppId: '63gcbm2jmskokurt5ku9fhejc6', // user pool app client ID
  userPoolDomain: 'domain.auth.us-east-1.amazoncognito.com', // user pool domain
});

exports.handler = async (request) => authenticator.handle(request);
environment
region Cognitoのリージョン
userPoolId メモしたuserPoolId
userPoolAppId メモしたuserPoolAppId
userPoolDomain ユーザプール > アプリケーションの統合 > ドメイン から取得(https:// 以降)


3. zip化

$ cd [任意のディレクトリ]/lambda_edge
$ zip -r lambda_edge.zip *

zipコマンドを実行するディレクトリを間違えるとLambdaが動作しないので注意。Lambdaデプロイ後index.jsが直下に存在しなければならない為。
また、モジュールのzip化についてはLambda Layerを使っても大丈夫と思いますが好みですね。

デプロイ
  1. マネジメントコンソールからLambdaを表示
  2. リージョンを「バージニア北部」に変更(Lambda@Edgeのデプロイはバージニア北部のみ)
  3. 関数の作成
    • 関数名:任意
    • ランタイム:Node.js 16.x (ローカル環境と合わせただけです)
  4. 準備したzipファイルをアップロード
  5. CloudFrontトリガーを使用するためのLambda実行ロールの編集
    • ロール名:任意
    • ポリシーテンプレート:基本的なLambda@Edgeのアクセス制限(CloudFrontトリガーの場合)
  6. Lambda@Edgeへのデプロイ
    • ディストリビューション:上記で作成済みのCloudFrontディストリビューションを指定
    • CloudFrontイベント:ビューアーリクエスト
    • ボディを含める:チェックあり
    • Lambda@Edgeへのデプロイを確認:チェックあり
動作確認
  1. CloudFrontのディストリビューションに戻って、ディストリビューションドメインをコピーしブラウザで表示
  2. Cognitoのログイン画面が表示されればOK(アクセストークンの有効期限時間内の場合はログイン画面が表示されない可能性あり。シークレットウィンドウなどで実施すると良い)
  3. ログイン

以上でCognito認証は完了です!Cognito連携最高〜〜〜〜〜!!!

Route53でドメインを持っていて、サブドメインを切ってWEBサイトを公開したい方はレコード追加やCloudFront、Cognitoの設定を一部変更します。
Route53使わないよという方は以上です。お疲れさまでした。

Route53 + Cognito + CloudFront + S3

Route53:(GPT4より)

  • 高い信頼性とパフォーマンス:Route 53は、AWSのグローバルなインフラストラクチャ上に構築されており、低遅延で高いパフォーマンスを提供します。また、冗長性のあるアーキテクチャにより、高い信頼性が確保されています。
  • 簡単なドメイン登録:Route 53を使用して、新しいドメインを登録したり、既存のドメインを移管したりすることができます。また、ドメインの管理や更新も簡単に行えます。
  • ヘルスチェックとフェイルオーバー:Route 53は、アプリケーションのヘルスチェックを実行し、問題がある場合に自動的にトラフィックを別のリソースに切り替えるフェイルオーバー機能を提供します。
  • トラフィックフロー:ルーティングポリシーを使用して、エンドユーザーのトラフィックを異なるリソースに振り分けることができます。例えば、地理的ルーティング、レイテンシーベースのルーティング、加重ルーティングなどが可能です。
  • AWSとの統合:Route 53は、Amazon EC2、Elastic Load Balancing、Amazon S3、Amazon CloudFrontなどの他のAWSサービスとシームレスに統合されており、簡単にDNS設定が行えます。

CloudFront

  1. ディストリビューションを表示。編集を選択。
  2. 代替ドメイン名(CNAME)を入力。SSL証明書は必要ですので設定してください。(作成方法は割愛します)

Route53

  1. マネジメントコンソールからRoute53 > ホストゾーン を表示。レコードを作成を選択。
  2. ルーティングポリシーは任意(本記事ではシンプルルーティング)
  3. シンプルなレコードを定義。レコード作成。
    • レコード名:任意
    • レコードタイプ:A
    • エンドポイントを選択:CloudFrontディストリビューションへのエイリアス
    • ディストリビューション:自身のディストリビューションを選択
      • もし候補が出てこない場合はCloudFront側の設定がうまく出来ていないです。

Cognito

  1. マネジメントコンソールからCognito > ユーザプール > アプリケーションの統合 > アプリクライアントと分析 で自身のアプリケーションクライアントを表示。
  2. コールバックURLをRoute53で設定したものに変えたいので編集します。
  3. コールバックURLをRoute53で作成したレコードに設定。(例: https://xxxxx.xxxxx.com)

動作確認

  1. Route53で設定したレコードにアクセス
  2. Cognitoの認証ページが表示され、ログイン後に期待通りのページが表示されればOK

以上でRoute53との連携も完了です!おつかれさまでした!

おわりに

元々自分用にS3でページを公開するのが目的でした。見られて困るコンテンツは無いのですが、誰でも見れる状態にするのにも抵抗があり今回Cognitoを導入しました。認証周りの知識は正直ゼロなのですが、殆どマネコンでポチポチで実現できるのが本当にありがたいです。

最後まで読んでいただきありがとうございました!良きCognitoライフを!

Discussion