ALB + Cognito で作るユーザー認証
概要
はじめに
- Webアプリケーションのセキュリティ対策として、不正アクセスを防御することは必要です。不正アクセスの防御には、WAF(Web Application Firewall)やIPフィルターなどを使用する方法もありますが、今回はALB(Application Load Balancer)にユーザー認証を実装する方法を紹介します。
- AWSドキュメントによれば、ALB のユーザー認証は、OIDC(OpenID Connect)準拠の外部ID プロバイダー(IdP)を利用するパターンと、Cognito のユーザープールを利用するパターンがあります。今回は、後者のALBにCognito のユーザープールを利用したユーザー認証を実装します。
- 上記の他に、ユーザー認証には、IDaaS(例: Auth0、Okta、OneLogin)を利用する方法やLambdaなど独自で実装する方法がありますが、Cognitoを利用することで手軽にユーザー認証が実装できます。
Cognito とは
- Amazon Cognito は、API ベースで実装されるWebアプリ、モバイルアプリに認証機能を提供します。ユーザーは、ユーザー名とパスワードを使用して直接サインインするか、Facebook、Amazon、Google、Apple などのサードパーティーを通じてサインインできます。
- Cognito には、ユーザープール と ID プールの機能があります。
- ユーザープールは、Amazon Cognito のユーザーディレクトリです。独自のユーザーディレクトリ or 外部ID プロバイダー(IdP)経由のログインに基づき、アプリへアクセスするためのトークンを提供します。
- ID プールは、Cognito ユーザープール or 外部ID プロバイダー(IdP)経由のログインに基づき、AWS のサービスにアクセスするための一時的な AWS 認証情報を提供します。
ALB + Cognito でBasic認証ライクな認証を構築する
システム構成
- 今回作成する構成は、以下となります。独自で認証機能を実装する場合、Lambda、Database など組み合わせ、認証機能の開発が必要になりますが、ALB + Cognito を利用すれば、以下の通りシンプルな構成で実現可能です。
ALB を準備する
- 先ず、ALB/EC2 を準備します。 ALB はinternet-facing にて作成し、HTTPS のリスナー作成およびSSL証明書を設定します。バックエンドはEC2 でなくとも構いません。
- 次に、ALB の名前解決のため、Route 53 にレコード登録を行います。DNSレコードの登録は、レコードタイプ “Aレコード" かつ “ALIAS" を選択します。レコードタイプにCNAME を指定した場合は、後続のCognito 独自ドメインの利用に支障があります。詳細は後述します。
- 最後に、WebブラウザからWebサーバーにアクセスできることを確認します。以下の例では、ALBのDNSに"niikawa-test.example.com" のFQDNを使用しています。アクセスした結果、テスト用に準備した"Hello World" が表示されました。
- ALB作成やACM 証明書発行の具体的な手順は、下記記事を参照ください。
Cognito のユーザープールを作成する
- Amazon Cognito のコンソールにアクセスし、「ユーザープールの管理」を選択します。
- 次に、「ユーザープールを作成する」を選択します。
- ユーザープールの「プール名」を入力します。ユーザープールの作成方法は、「デフォルトを確認する」を選択します。
- 設定値を確認し、「プールの作成」を押します。なお、「属性」で設定するサインインオプション、属性オプションは、プール作成後は変更できません。ユーザープール作成前に、サインインに関する設定が目的に合致することを確認しましょう。
- また、ユーザーにサインアップさせるのではなく、管理者の作成を必須とする場合、ユーザーのサインアップを許可する設定に「管理者のみにユーザーの作成を許可する」を選択します。
- 以下の通り、ユーザープールが作成されました。
サインインページに独自ドメインを設定する
独自ドメインの設定
- 事前に、ACM のバージニア北部のリージョンにSSL証明書の作成が必要です。
- ユーザープール作成後、Cognitoのサインインページに独自ドメインを設定します。「アプリの統合」→「ドメイン名」を選択します。
- 「ドメインの使用」ボタンを押します。ドメイン名、ACM の証明書を指定し、「変更の保存」ボタンを押します。以下は、「変更の保存」ボタンを押した後の画面です。
- 数分経過後に、「CREATING」の右にあるアイコンをクリックし、再読み込みを行います。ドメインのステータスが ACTIVE に変わりました。以下は、ACTIVE に変わった後の画面です。
- 画面下部に表示されているエイリアスターゲットの DNS名を使用し、Route 53 にCNAME のレコードを登録します。
注意事項
- サインインページに独自ドメインを設定する際に注意事項があります。サインインページに割り当てる独自ドメインには、サブドメインを作成して割り当てることです。例えば、ALBに割り当てたFQDNが “niikawa-test.example.com" の場合、サインインのページは、"auth.niikawa-test.example.com" を割り当てます。理由は、下記ドキュメントに記載の通り、DNS には“Aレコード" が設定されている必須条件があります。そのため、事前準備で登録したALB のDNS に、レコードタイプ “Aレコード" かつ “ALIAS" を選択した理由はこの必須条件を解消するためです。
引用元: Amazon Cognito Developer Guide
- A web domain that you own. Its root must have a valid A record in DNS. For more information see Domain Names.
- The ability to create a subdomain for your custom domain. We recommend using auth as the subdomain. For example:
auth.example.com
.
- もしドメインを設定する際に、DNSにAレコードが設定されていなければ、下記のエラーメッセージが表示されます。
- Custom domain is not a valid subdomain. Was not able to resolve the root domain. please ensure an A record exists for the root domain.
ユーザープールにアプリクライアントを作成する
- ALB にアクセス権限を提供するため、ユーザープールにアプリクライアントを作成します。「アプリクライアント名」を入力し、「アプリクライアントの作成」ボタンを押します。
- アプリクライアント ID が表示されました。
アプリクライアントを設定する
- ID プロバイダを選択します。
- コールバック URLには、パス「/oauth2/idpresponse」の付加が必要になります。今回は、サインアウト URLは指定しません。
- OAuth フローには、「Authorization code grant」、OAuth スコープには、「openid」を選択します。
- 「変更の保存」を押します。
ALB のリスナールールに認証を設定する
- ALB を選択し、リスナータブを開きます。対象リスナーの「ルールの表示/編集」を選択します。
- 編集のマークを選択し、ルールの編集を行います。ルールのTHEN に登録されている既存の転送先は、削除します。
- ルールのTHEN にて「アクションの追加」→「認証…」を押します。
- 「認証」に「Amazon Cognito」、「Cognito ユーザープール」に作成済みのユーザープールのプール ID、「アプリクライアント」に作成済みのアプリクライアントのアプリクライアント ID を選択します。
- 「転送先」にターゲットグループを選択します。「更新」ボタンを押します。
注意事項
- ユーザープールに独自ドメインが設定されていなければ、リスナールールに認証が選択できません。また、同様にユーザープールにアプリクライアントが設定されていなければ、リスナールールに認証が選択できません。
- アプリクライアントの設定がされていなければ、リスナールールの更新時に下記のエラーが表示されます。前述のアプリクライアントの設定において、ID プロバイダ、コールバック URL、OAuth フローおよびスコープを設定します。
- OAuth flows must be enabled in the user pool client
アクセス確認
- ALBに割り当てたFQDN(例: https://niikawa-test.example.com/)へのアクセスを確認します。以下サインインの画面が表示されれば成功です。
注意事項
- 「500 Internal Server Error」が表示された場合、Cognitoのサインインページに設定した独自ドメインのDNS名が未だレコード登録されていないかもしれません。「アプリ統合」→「ドメイン名」にてエイリアスターゲットの DNS名を確認し、CNAME のレコードを登録します。
- 「An error was encountered with the requested page.」のエラーが表示された場合、アプリクライアントの設定において、コールバック URLのパスに誤りがある可能性があります。前述のアプリクライアントの設定において、コールバック URLにパス「/oauth2/idpresponse」が付加されていることを確認します。
Cognito ユーザープールのユーザー作成方法
- 先の手順で作成したユーザープールにユーザーを作成します。ユーザー作成は、「ユーザーの作成」or「ユーザーをインポート」のどちらかで行います。
個別にユーザーを作る手順
- 「ユーザーの作成」を押し、個別にユーザーを作成します。ユーザー名、仮パスワード、E メールなどを指定します。
- 指定したE メールのアドレスに、仮パスワードが送信されました。
- ALBに割り当てたFQDN(例: https://niikawa-test.example.com/)へアクセスします。パスワードを変更します。
- 無事に認証をpassし、テスト用のWebにアクセス確認が出来ました。
まとめてユーザーをインポートする手順
- 「ユーザーをインポート」を押し、まとめてユーザーをインポートします。先ず、「CSV ヘッダーのダウンロード」を押し、テンプレートをダウンロードします。
- 次に、CSV ファイルにユーザー情報を記入します。以下は、最低限の情報を記入した CSV のサンプルです。
name,given_name,family_name,middle_name,nickname,preferred_username,profile,picture,website,email,email_verified,gender,birthdate,zoneinfo,locale,phone_number,phone_number_verified,address,updated_at,cognito:mfa_enabled,cognito:username
,,,,,,,,,niikawa@example.com,TRUE,,,,,,FALSE,,,FALSE,niikawa
- 次に、「インポートジョブの作成」を選択します。「ジョブ名」を入力し、IAM 名を指定します。(初回実行の場合はIAM のロール名を指定し、2回目以降の場合はIAM ロールを選択する)
- 先ほど作成したCSV ファイルをアップロードします。最後に、「ジョブを作成する」を押します。
- ジョブのステータスが「Created」になります。右端のメッセージにある「開始」を押します。次に、ジョブのステータスが「Pending」に変わるため、再読み込みのアイコンを押します。
- ジョブのステータス「Succeeded」に変わり、右端のメッセージに「Import Job Completed Successfully.」が表示されれば成功です。
- インポートがFailed となった場合は、CSV ファイルを修正します。エラーの理由は、CloudWatch Logs にて確認可能です。(以下の画面は、成功時のログとなります)
- 例えば、CSV ファイルのファイル名を変更すると「The header in the CSV file does not match the expected headers. Use the GetCSVHeader API to get the expected headers.」のエラーとなります。ファイル名は、デフォルトの “headers.csv" を使用します。
- インポートが完了しました。以下の通り、インポートジョブで作成されたユーザは、アカウントのステータスが「RESET_REQUIRED 」になります。
- ALBに割り当てたFQDN(例: https://niikawa-test.example.com/)へアクセスします。ユーザーのログイン時に「Forgot my password」を押し、ユーザ名を入力後、「Reset my password」を押します。
- メールに通知されたreset code を入力し、パスワードを設定します。
- 無事に認証をpassし、テスト用のWebにアクセス確認が出来ました。
- 上記方法であれば、管理者が複数ユーザーの初期パスワードを設定する必要なく、ユーザーは利用できます。
その他
- サインインページのUI をカスタマイズすることも可能です。色々とお試しください。