Amazon CognitoユーザープールLambdaトリガーでALB認証のメールアドレスを制限する

ども、大瀧です。
先日リリースされたALBの認証機能、ブログではGoogle認証と連携する様子をご紹介しました。

ただこの構成は任意のGoogleアカウントの認証を通してしまうので、Google Appsの自組織のメールアドレスのみ許可したいというような業務向けのユースケースだとターゲット側で認可機能を実装しなくてはならず、片手落ち感がありました。

そこで本記事では、認証先にCognitoユーザープールを設定し、Lambdaトリガーでメールアドレスを制限する構成をご紹介します。

構成の目的と概要

ALBの認証機能を用いて、Webアプリケーションへのアクセスを制限します。前回の記事で指定したOpenID ConnectによるGoogle認証の代わりにAmazon Cognitoを指定し、そのCognitoユーザープールの外部IDプロバイダとしてGoogle認証を連携させます。

わざわざCognitoユーザープールを経由させた理由は、Cognitoユーザープールには認証プロセスの途中でAWS Lambdaの関数を呼び出すトリガーという機能があり、その関数で今回のテーマ、メールアドレスを確認&制限させたかったためです。

事前準備

Google認証を呼び出すために、あらかじめGoogle Identity PlatformでOAuth2.0 クライアントIDを発行する必要があります。前回の記事のステップ1と同様のため、本記事では割愛します。承認済みのリダイレクトURIに以下を設定し、クライアントIDとクライアントシークレットを控えておきましょう。

https://<Cognitoユーザープール作成時に指定するプレフィックス>.auth.<Cognitoユーザープールのリージョン名>.amazoncognito.com/oauth2/idpresponse

ステップ1 ALBにCognitoユーザープールの認証を追加する

ALBのHTTPSリスナにCognitoユーザープールの認証設定を追加します。今回は認証設定と一緒にユーザープール自体も新規作成し、Google認証もそこで設定しちゃいます。事前にHTTPSリスナの作成とTLS証明書を設定し、ターゲットと正常にHTTPSで疎通できることを確認しましょう。前回の記事を設定済の場合は、認証設定を削除しておけばOKです。

ALBのHTTPSリスナ画面を開き、左上の鉛筆アイコンをクリックして、「最後」のルールの左にある鉛筆アイコンをクリック、ルールを編集します。

ルールのTHEN右下の[+ アクションの追加] - [認証...]を選択します。

[認証]は「Amazon Cognito」、[Cognitoユーザープール]は「新規作成」を選択します。

外部IDプロバイダの選択項目が出てくるので、ここで事前に準備したGoogle認証の情報を入力します。

  • ソーシャルIDプロバイダ : Google
  • アプリケーションID : Google OAuth2.0 クライアントIDのクライアントID
  • アプリシークレット : Google OAuth2.0 クライアントIDのクライアントシークレット
  • 承認スコープ : email
  • ドメインのプレフィックスとユーザープール名 : 任意(ドメインは一意にする必要あり)

ここでのポイントは承認スコープで、メールアドレスをCognitoユーザープールで取得するためにemailを指定しています。

[Cognitoユーザープールの作成]ボタンをクリック、画面右上の[更新]ボタンをクリックすればOKです。

ステップ2 トリガーとしてのLambda関数を作成する

続いて、Cognitoユーザープールのトリガーとして呼び出すLambda関数を作成します。ランタイムは特に問いません。Cognitoからはイベントオブジェクトにユーザーアトリビュートのemailとしてメールアドレスが渡されるのでそれをチェックし認証を通す場合はイベントオブジェクトを含めたコールバック、通さない場合は実行エラーとすればOKです。

  • 関数名 : cognito-userpool-trigger-test
  • ランタイム : Node.js 6.10
exports.handler = (event, context, callback) => {
    if (event.request.userAttributes['email'].match(/<許可するドメイン名>$/) == null) {
        var error = new Error('Email not allowed');
        context.done(error, event);
    }
    // return the result to Amazon Cognito
    callback(null, event);
};

実行エラーは後述の動作検証の通り、アクセスするブラウザの表示では5xxエラーになってしまうので、もっとスマートな認証失敗の実装方法があれば知りたいところです。403とか。

ステップ3 Cognitoユーザープールにトリガーを追加する

Cognitoユーザープールでトリガーの追加とGoogle連携のための設定を少々行います。ステップ1で作成したCognitoユーザープールの管理画面を表示し、メニューの[全般設定] - [トリガー]にある「サインアップ前」のLambda関数でステップ2で作成したLambda関数を選択、画面下部の「変更の保存」ボタンをクリックします。

メニューの[フェデレーション] - [属性マッピング]画面から[Google]タブをクリック、Google属性の「email」のチェックをオンにしユーザープール属性の「Email」を選択、[変更の保存]ボタンをクリックします。

メニューの[アプリの統合] - [アプリクライアントの設定]画面を表示し、[コールバックURL]のドメイン名をALBに設定したカスタムドメイン名に変更し、[変更の保存]ボタンをクリックします。

これでOKです。

動作確認

WebブラウザからALBにアクセスすると...

Googleの認証画面が表示されました。ためしに許可するドメインのメールアドレスではない、他のGmailアカウントで認証してみると...

認証がエラーになりました!不格好なステータスコード&エラーメッセージですが、目的は達成されました!

まとめ

ALB認証にCognitoユーザープールを指定し、トリガーのLambda関数でメールアドレスの制限機能を実装しました。一般公開するWebサービスで使うのは難しいですが、社内向けWebアプリケーションに後付けで認証機能を追加する実用的な方法として参考になれば幸いです。

これでGoogle認証についてはCloud IAPとほぼ同等になったかなと思います。ALBは他のIdPも利用できるのが良いですね。

参考URL