Cognito Userpool から送信される認証メール・SMSの送信基盤が選べるようになりました


AWS の認証基盤サービスである Cognito Userpool では、メールアドレスの実在性を確認したり、パスワードリセットを行うため、確認コードをメールやSMSで送信する機能が提供されています。

これまで、それらのメッセージの内容をカスタマイズするためには、以下の方法が提供されていました。

  • コンソールからメッセージを編集する
  • カスタムメッセージLambdaトリガーを利用する

これらのメッセージは、Amazon SESやAmazon SNSを利用して送信されていましたが、2020年11月のサイレントアップデートにより、サードパーティーの配信基盤(SendGrid等)からメッセージを送信する手段が提供されました!

また、SESを利用する場合でも、プレーンテキストやマルチパート形式でメールを送信したり、SESのConfiguration Setを適用することが可能になります。
一部のキャリア・受信設定によってはHTMLパートのみのメールが正しく受け取れずに文字化けしたりすることがありますが、これも解消することが可能です!

それでは早速、これを適用するための新しいトリガー(以下「カスタムメッセージ送信Lambdaトリガー」の設定手順を紹介します。

ポイント

  • カスタムメッセージLambdaトリガーでは「確認コードを配置するためのプレースホルダー」が渡されるが、カスタムメッセージ送信Lambdaトリガーでは「KMSキーで暗号化された確認コード」が渡される
  • Lambda内で復号処理を実施し、メッセージを送信するコードを書く
  • コンソールやCloudFormationからは設定できないので、CLIを利用する(2021年1月現在)

設定手順

以下のドキュメントを参考に進めていきます(この記事ではドキュメントと異なるステップで設定を進めます)。
https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-custom-email-sender.html

なお、ここではEメールの送信について設定していきますが、SMSについても同様の手順で設定できます。

1. KMSでキーを作成する

KMSで、確認コードの暗号化に使用する対称キーを作成します。

キーのARNと、エイリアスのARNを控えておきます。

2. Lambdaを作成する

ユーザー情報と確認コードをCognitoから受け取り、メールを送信するLambdaを作成します。
以下、上記ドキュメントに記載のサンプルコード(Node.js)を整形したものを記載します。


const AWS = require('aws-sdk');
const b64 = require('base64-js');
const encryptionSdk = require('@aws-crypto/client-node');

//Configure the encryption SDK client with the KMS key from the environment variables.
const { encrypt, decrypt } = encryptionSdk.buildClient(encryptionSdk.CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT);
const generatorKeyId = process.env.KEY_ALIAS;
const keyIds = [process.env.KEY_ID];
const keyring = new encryptionSdk.KmsKeyringNode({ generatorKeyId, keyIds })

exports.handler = async (event) => {
  //Decrypt the secret code using encryption SDK.
  let plainTextCode;
  if (event.request.code) {
    const { plaintext, messageHeader } = await decrypt(keyring, b64.toByteArray(event.request.code));
    plainTextCode = plaintext
  }

  //PlainTextCode now has the decrypted secret.

  if (event.triggerSource == 'CustomEmailSender_SignUp') {

    //Send email to end-user using custom or 3rd party provider.
    //Include temporary password in the email.

  } else if (event.triggerSource == 'CustomEmailSender_ResendCode') {

  } else if (event.triggerSource == 'CustomEmailSender_ForgotPassword') {

  } else if (event.triggerSource == 'CustomEmailSender_UpdateUserAttribute') {

  } else if (event.triggerSource == 'CustomEmailSender_VerifyUserAttribute') {

  } else if (event.triggerSource == 'CustomEmailSender_AdminCreateUser') {

  } else if (event.triggerSource == 'CustomEmailSender_AccountTakeOverNotification') {

  }

  return;
};

サンプルLambdaについての補足

サンプルLambdaではaws-sdkに加えて@aws-crypto/client-nodebase64-jsというライブラリを使用しています。
npm等を使ったインストールや、Lambdaデプロイ時にアップロードが必要となります。

このサンプルLambdaを動作させるには、環境変数を2つ設定する必要があります。
それぞれ変数名から想起される内容と設定すべき値が異なるのでご注意ください。

  • KEY_ALIAS: KMS対称キーのエイリアスのARN
  • KEY_ID: KMS対称キーのARN

Lambdaの実行ロールには、通常のLambda実行権限のほかに kms:Decrypt アクションを許可する必要があります(リソースに上記キーを設定するなど、適切に権限付与を行ってください)。

また、サンプルLambdaでは送信処理の記述は割愛されていますので、お使いの配信基盤に合わせて送信処理を実装してください。

トリガーLambdaの仕様について

event.triggerSource で渡される値については上記ドキュメントに記載があり、また従来のカスタムメッセージトリガーとほとんど変わりません(加えて、新しいトリガーでは「アドバンスドセキュリティを有効にした場合の高リスク通知メール」についてもカスタマイズできるようになっています)。

暗号化された確認コードが格納されている event.request.code のほかは、カスタムトリガーの共通パラメータに記載の内容が渡されます。
例えばメールアドレスは event.request.userAttributes.email に、電話番号は event.request.userAttributes.phone_number に格納されていますので、これらを使ってメッセージの送信を行うことができます。

Lambdaを作成したら、LambdaのARNを控えておきます。

3. Cognito Userpool から Lambda を呼び出すための実行権限を設定する

以下のコマンドを実行し、Cognito Userpoolから該当のLambdaを呼び出せるようにします。
<lambda_arn> には、先ほど作成したLambdaのARNを指定します。

aws lambda add-permission \
  --function-name <lambda_arn> \
  --statement-id "CognitoLambdaInvokeAccess" \
  --action lambda:InvokeFunction \
  --principal cognito-idp.amazonaws.com

4. Userpool にトリガーを設定する

Userpoolでメッセージを送信するタイミングで、作成したLambdaが呼ばれるようにトリガーを設定します。

  • Eメールの送信で設定するトリガー: CustomEmailSender
  • SMSの送信で設定するトリガー: CustomSMSSender

しかし、2021年1月現在ではこのトリガーをコンソールやCloudFormationで設定することができません。AWS CLIを使用した手順がドキュメントに記載されています。

# 既存の設定の確認
aws cognito-idp describe-user-pool --userpool-id <userpool_id>

# トリガーの反映
aws cognito-idp update-user-pool \
  --userpool-id <userpool_id> \
  --lambda-config "CustomEmailSender={LambdaVersion=V1_0,LambdaArn= <lambda_arn> },KMSKeyID= <key_id>"

トリガーを設定するにはupdate-user-poolコマンドを使用します。1

  • <userpool_id>: UserpoolのプールID
  • <lambda_arn>: 先ほど作成したLambdaのARN
  • <key_id>: KMS対称キーのARN 2

このコマンドに関してはいくつか注意点があります。

  • これらのトリガーは比較的最近のバージョンのAWS CLIでのみ実行可能です。バリデーションエラーが発生した場合はバージョンをご確認ください。3
  • ほかのトリガーを設定している場合(確認完了後、認証チャレンジ、ユーザー移行等)は、それらも一緒に --lambda-config に設定する必要があります。 CustomEmailSender のみを指定して上記コマンドを実行すると、それ以外のトリガーがすべて外れます。
  • Lambda ARNを直接指定するほかのトリガーと異なり、今回増えた CustomEmailSenderCustomSMSSender の場合は LambdaVersionLambdaArnによるオブジェクトをセットする必要があります。
  • LambdaVersionV1_0 固定です。Lambda自身のバージョンを指定できるパラメータではありません。

以上で設定は完了です。

まとめ

AWS What's Newで見つけることができず発見が遅くなってしまったのですが4、待っていた方も多いアップデートなのではないでしょうか。
これまでのカスタムメッセージLambdaでは不満だった方は、ぜひこのアップデートを試してみてください!


  1. ドキュメントではなぜかコマンド部分が割愛されています。なぜ…… 

  2. ARNを指定するのにパラメータ名はKmsKeyIDなので注意。なぜ…… 

  3. このコマンドを実行するために CloudShell を使っていたのですが、 CloudShell 上のデフォルトのAWS CLIのバージョンでは古くて実行できませんでした(CloudShell上のAWS CLIも、ドキュメントに記載の手順でバージョンアップが可能です)。 

  4. 改めて探してみたのですが、2020年11月にはそれらしきアップデートが見つからず。今回はたまたま英語版のドキュメント更新履歴を見て発見しました。それにしてもなぜこのアップデートだけ更新日が「November 2020」なんでしょう……?