今回は以前ご紹介した記事「HRMOS」の従業員データベースシステムのAWSリージョンをオレゴンから東京に移行した話の中でも少し触れていた、 「Amazon Cognito user pools ユーザーの別アカウントへの移行」 について、具体的にどのように行ったかをご紹介します。

Amazon Cognito user pools のアカウント間移行は、我々にとって初めての経験の連続でした。世の中で発信されている情報も少なく、社内にも知見が無いため、AWS サポートの方と連携を取りながら進める必要のあるものでした(対応可不可の判断の多くで AWS サポートへの確認も行っています)。この貴重な経験がお役に立てると思い、今回の取り組みで得た知見を共有させていただきます。

パスワード再設定などのユーザーアクションを極力なくすためにどのような移行方法をとれるかを検討

要件

移行に関係する仕様と移行前の利用状況は以下の通りです(Amazon Cognito user pools の細かい設定は割愛します)。

今回オレゴンリージョンで構築していた Amazon Cognito user pools の全ユーザーを、 上記の要件を満たしつつ 東京リージョンに構築した Amazon Cognito user pools に移行する必要がありました。 その際、当然要件にはパスワードの再設定など ユーザーに対して特別なアクションを極力発生させないこと も含まれていました。

方針検討

AWSでは Amazon Cognito user pools のユーザーを別の Amazon Cognito user pools に移行する方法として以下の2つを提供しており、今回は 「ユーザー移行 Lambda トリガーを使用したユーザーインポート」 の方法を採用しました。

「 CSV ファイルからのユーザーインポート」は移行元の user pools から パスワード以外の すべてのユーザー情報を CSV ファイルにエクスポートし、それを移行先の user pools にインポートすることで移行を実現します。移行方法自体はシンプルなのですが、この方法の場合は移行後に すべてのユーザーにパスワードを再設定してもらう必要があります。 これは要件の 「ユーザーに対して特別なアクションを極力発生させないこと」 に反しているので採用しませんでした。

ユーザー移行 Lambda トリガーの利用

この方法は Amazon Cognito user pools でいくつか用意されている Lambda トリガーのうち、「ユーザー移行の Lambda トリガー」を利用した移行方法で、 Amazon Cognito user pools のサインアップフローで動作します。 具体的には、 パスワードによるサインイン時 または パスワード再設定時 にユーザーが移行先の user pools に存在しない場合に、設定した Lambda 関数が動作する仕組みです。 関数が適切なレスポンスを返却すると、移行先の user pools に パスワードを引き継いだ状態 で対象のユーザーが移行されます。

関数のリクエスト・レスポンスパラメータとしては色々あるのですが、今回利用したものは以下になります。

リクエスト

パラメータ名
TriggerSource UserMigration_Authentication (サインイン時) または UserMigration_ForgotPassword (パスワードリセット時)
UserName 入力されたユーザー名
Password 入力されたパスワード(パスワードリセット時は設定されない)

レスポンス

パラメータ名
UserAttributes Amazon Cognito user pools のユーザー属性として記録される値
FinalUserStatus 移行後のユーザーの状態。 CONFIRMED または RESET_REQUIRED
MessageAction 移行後にウェルカムメッセージを送信するかどうか。SUPPRESS を設定するとウェルカムメッセージを送信をしない

処理内容

サインインイベント( TriggerSource が UserMigration_Authentication)時のみ、リクエストされたユーザー名とパスワードで移行元の user pools に認証できることを確認する必要があります。

パスワードリセットイベント( TriggerSource が UserMigration_ForgotPassword)時はリクエストにパスワードが含まれていないため、移行元の user pools にはユーザーが存在することの確認のみを行います。

  1. 移行元の user pools に AdminInitiateAuth をリクエストし、認証できるかチェックします。(サインインイベント時のみ)
  2. ユーザー属性をそのまま移行したいので、移行元の user pools に AdminGetUser をリクエストして該当ユーザーの情報を取得します。
  3. 関数のレスポンスとして以下をセットして処理を終了します。
    • UserAttributes
      • username : 移行先の user pools 用に新規発行したUUIDをセット
      • email : ユーザーのメールアドレスをセット
      • email_verified : メールアドレスの確認は済んでいる前提なので true 固定でセット
      • phone_number : AdminGetUser で取得した情報をそのままセット(存在しない場合もあり)
      • phone_number_verified : AdminGetUser で取得した情報をそのままセット(存在しない場合もあり)
    • FinalUserStatus : パスワードをそのまま利用したいので CONFIRMED をセット
    • MessageAction : ウェルカムメッセージは不要なので SUPPRESS をセット
ユーザー移行 Lambda 関数の処理
ユーザー移行 Lambda 関数の処理

注意点

ユーザー移行 Lambda 関数で利用するパスワードを クライアントから送信してもらう必要がある ため、Amazon Cognito user pools の認証フローとして USER_PASSWORD_AUTH を設定しなければなりません。 一般的には、パスワードを送信しないことから、よりセキュアな認証フローである USER_SRP_AUTH を設定していることが多いと思いますので、移行時のみ USER_PASSWORD_AUTH を設定し、移行が終わり次第 USER_SRP_AUTH に戻すと良いでしょう。

MFA(SMS)の対応

ここまででユーザー移行 Lambda トリガーを利用して、移行元の user pools からユーザーを順次移行していく仕組みができましたが、このままでは MFA を利用しているユーザーがログイン時に2段階目の認証をパスすることができませんでした。 調査の結果、ユーザー移行 Lambda トリガー内で MFA(SMS) を有効化することはできないことが判明したため、シームレスな移行を実現するために 認証前 Lambda トリガー を利用することにしました。

認証前 Lambda トリガー

認証前 Lambda トリガーとは、Amazon Cognito user pools のサインイン処理の前に Lambda 関数が実行されるトリガーです。ここで MFS(SMS) を有効化することができました。

補足として、ユーザー移行 Lambda トリガーと同時に利用している場合は ユーザー移行 Lambda トリガーが先に実行され、その後 認証前 Lambda トリガーが実行されます。 ただし、ユーザー移行 Lambda トリガーは基本的に 1ユーザーにつき1度しか実行されない のに対し、認証前 Lambda トリガーは 毎回実行される 点は考慮が必要です。

今回利用した関数のリクエスト・レスポンスパラメータは以下になります。

リクエスト

パラメータ名
UserAttributes ユーザー属性

レスポンス

なし

処理内容

ここでやるべきことは MFA(SMS) を有効にすることです。 MFA(SMS) を有効にすべきユーザーを判定して、 AdminSetUserMFAPreference をリクエストします。

MFA(SMS) を有効にすべきユーザーの判定方法は、先行して実行されるユーザー移行 Lambda 関数の中でカスタム属性を設定することで解決しました。

  1. ユーザー移行 Lambda 関数で、取得したユーザー情報を精査し PreferredMfaSetting == "SMS_MFA" が設定されている場合、 UserAttributes にカスタム属性として custom:mfa_setting: SMS_MFA をセットします。
  2. 認証前 Lambda 関数で、UserAttributes にカスタム属性 custom:mfa_setting: SMS_MFA が見つかった場合のみ AdminSetUserMFAPreference をリクエストします。
  3. カスタム属性 custom:mfa_setting: SMS_MFA はもう不要なので削除しておきます。
MFA(SMS) に対応したユーザー移行処理
MFA(SMS) に対応したユーザー移行処理

注意点

Amazon Cognito user pools における属性変更などの処理は結果整合性的に反映が行われる動作となっているため、ごく稀に反映遅延により MFA コードの入力を求められない可能性があります。

その時々の状況によって発生頻度が変わる可能性はありますが、我々の開発環境で何度かテストした結果ではほぼ100% MFA コード入力が求められ、入力後正常にログインすることができていました。 そのため、可能性はゼロではないと知りつつも今回の移行では MFA を利用しているユーザーがそれほど多くないこと、再度ログインを試みることで解消できることからこの件は容認しました。

やむを得ずユーザー負担がかかってしまったところ

MFA(Application) はユーザーに再設定を求めた

移行要件として、 ユーザーに対して特別なアクションを極力発生させないこと としていたのですが、 MFA(Application) は Amazon Cognito user pools とは関係のない機能ということもあり、 移行後にユーザーに再設定を求める ことになりました。

MFA(SMS) 利用ユーザーは認証コードが2回届く

MFA(SMS) を利用しているユーザーがこの移行プロセスを通過する際、以下のタイミングで認証コードが送信されます。

  1. ユーザー移行 Lambda 関数で実行している移行元の user pools への認証処理
  2. 移行先の user pools に対する MFA(SMS) の有効化後に実行される認証処理

これによって、 ユーザーは2回認証コードを受け取ることになり、2回目に届いたコードを入力してもらう 必要がありました。 ユーザーからするとややわかりにくい挙動となってしまいましたが、 移行後の初回のみかつ、 MFA(SMS) 利用ユーザー数は少ない ということから容認することにしました。

まとめ

やむを得ず容認せざるを得ないこともいくつかあったものの、ユーザー移行 Lambda トリガーと認証前 Lambda トリガーを利用することで、 MFA(SMS) にも対応した極力ユーザーアクションを発生させない Amazon Cognito user pools のユーザー移行が実現できました。

Amazon Cognito user pools には途中で変更できない設定項目もいくつか存在し、サービスの運用途中でその設定を変更したいという要件が出てきた場合は Amazon Cognito user pools 自体を再作成する必要があります。そのため、今回のような AWS リージョン移行に限らず Amazon Cognito user pools のユーザー移行が必要な場面もあるのではないかと思います。

なるべくならそのような変更が起きないようにしっかり設計をしておくべきではあるのですが、プロダクトを取り巻く様々な状況の変化によって移行せざるを得ないケースに直面することもあるでしょう。 その際にはぜひこの記事を思い出し、シームレスに移行させる手段としてユーザー移行 Lambda トリガーを利用できることやどのように利用すればいいのかなどを考える際の参考として、少しでも役立てていただけると嬉しいです。

阪口 透
阪口 透

HRMOSでSRE・マネージャー・スクラムマスターなど雑多にやらせてもらっています。