概要

Amazon Cognitoを利用してログイン機能実装することがあると思いますが
その機能に必要不可欠なパスワードリセットで
MFA認証をEメールのコードで認証している場合、パスワードリセットのEメールを受信できない仕様のようです。

ユーザーが希望する MFA の方法は、パスワードの復旧に使用できる方法に影響します。希望する MFA を E メールメッセージにしたユーザーは、パスワードリセットコードを E メールで受信できません。希望する MFA を SMS メッセージにしたユーザーは、パスワードリセットコードを SMS で受信できません。

引用:Amazon Cognitoデベロッパーガイド

下記に対処方法を記載しておきます。(環境はLambda、言語はpython)

対処方法

1. 対象のユーザープールアプリクライアントID、クライアントシークレット、ユーザープールID、パスワードリセットするEメールアドレスを取得する

Lambdaの環境変数にキーと値を設定し、環境変数から値を取得します。
※今回例のため簡単に取得できるようにしてあります。Secrets Manager等を利用した方がセキュリティ的に安全です。

環境変数を用意
ユーザープールアプリクライアントID(「ユーザープール」>「アプリケーションクライアント」タブの 「クライアント ID」から確認できます。)
キー:CLIENT_ID
値:xxxxxxxx

・クライアントシークレット(「ユーザープール」>「アプリケーションクライアント」タブの 「クライアントシークレット」から確認できます。)
キー:CLIENT_SECRET
値:xxxxxxx

・ユーザープールID(「ユーザープール」タブの「ユーザープール ID」から確認できます。)
キー:USER_POOL_ID
値:ap-northeast-1_xxxxxxxxx

# 環境変数から取得

import os

client_id = os.environ.get('CLIENT_ID') # ユーザープールアプリクライアントID
client_secret = os.environ.get('CLIENT_SECRET') # クライアントシークレット
user_pool_id = os.environ.get('USER_POOL_ID') # ユーザープールID

# パスワードリセットするEメールアドレスはリクエストパラメータから取得
email = request['email'] 

2. 該当するユーザーのMFA認証を一時的に無効化

該当ユーザーのEメールMFA設定を無効化。
これにより、パスワードリセットメールが受信できるようになります。

response = client.set_user_mfa_preference(
    EmailMfaSettings={
        'Enabled': False, # Email MFAが無効
        'PreferredMfa': False # Email MFAが優先MFA方式ではない
    },
    UserPoolId=user_pool_id, # ユーザープールID(string)
    Username=email # Eメールアドレス(string)
)

3. パスワードリセットを実行

MFA無効化後、通常通りパスワードリセット処理を実行します。
この時点でユーザーにパスワードリセットメールが送信されます。

下記の値を使ってシークレットハッシュ 値の計算し、クライアントシークレットハッシュ値を用意します。
・Eメールアドレス
・ユーザープールアプリクライアントID
・クライアントシークレット
シークレットハッシュ値の計算方法

response = client.forgot_password(
    ClientId=client_id # ユーザープールアプリクライアントID(string)
    SecretHash=secret_hash, # クライアントシークレットハッシュ値(string),
    Username=email # Eメールアドレス(string)
 )

4. パスワードリセット実行後、MFA認証を再度有効化

ユーザーがパスワードリセットを完了した後、
セキュリティ確保のためEメールMFA設定を再び有効化にします。

response = client.set_user_mfa_preference(
    EmailMfaSettings={
        'Enabled': True, # Email MFAが有効
        'PreferredMfa': True # Email MFAが優先MFA方式
    },
    UserPoolId=user_pool_id, # ユーザープールID(string) 
    Username=email # Eメールアドレス(string)
)

まとめ

ログイン機能のMFA認証は現代では必須級なので
意外とここでつまづくことがあるのではないかと思い、今回簡単に記事にさせていただきました。
少しだけですがAmazon Cognito触ってみて、少しクセがあるなと感じたので
もっと記事を増やしていけたらと思います。