【AWS】Cognitoのキャリア認証メールの文字化け対応

# キャリアメール文字化け
CognitoIdentityServiceProviderのadminUpdateUserAttributes関数を用いてメール認証やメール変更を行なっているところ、キャリアメールの場合文字化け現象が起きてしまいました。

原因はキャリアメールの場合、OSのメッセージアプリを使ってメール受信するから、送信者と受信者でメールソフトのエンコードが異なると文字化けが発生してしまいます。

https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#adminUpdateUserAttributes-property

# SESマルチパート配信
SESを用いて、マルチパート配信すれば、解決できます。SESマルチパートについて、前の記事に書きましたので、ここでは述べません。

https://qiita.com/ling350181/items/4988c703befa4c9c818d#%E3%83%9E%E3%83%AB%E3%83%81%E3%83%91%E3%83%BC%E3%83%88%E9%85%8D%E4%BF%A1sendrawemail

# アーキテクチャー
CognitoIdentityServiceProviderのadminUpdateUserAttributesでカスタムEメール送信者Lambdaトリガー起動させて、Lambda関数でSESマルチパート配信を行う
![スクリーンショット 2023-10-12 10.37.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/12884664-c4d5-d0fb-205d-8e528534b312.png)

# Cognito　Lambdaトリガーによるマルチパート配信
## 注意点
:::note warn
1. カスタムEメール送信者LambdaトリガーはコンソールやCloudFormationから作成できないので、AWS CLIを利用する
1. カスタムメッセージLambdaトリガーでは「確認コードを配置するためのプレースホルダー」が渡されるが、カスタムEメール送信者Lambdaトリガーでは「KMSキーで暗号化された確認コード」が渡される
1. 渡されたコードを復号処理しないといけない
:::

本件と関係ないですが、追伸となります：

:::note info
カスタムメッセージLambdaトリガーでメール送信する際、 Lambda関数にてメール送信処理まで実施する必要はないです。カスタムメッセージレスポンスパラメータを　Lambda　関数での return としていただけますと、その内容に基づいて Cognito 側がメール送信処理を実施します。
```json
"response": {
    "smsMessage": "string",
    "emailMessage": "string",
    "emailSubject": "string"
}
```
:::
## KMS Keyを作成する
AWS Key Management Service (AWS KMS) で対称暗号化キーを作成します
![スクリーンショット 2023-10-12 10.55.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/5c5b76a9-4a6c-0fc7-2320-9ddb7a55f2c1.png)

## Lambda作成
カスタム送信者のトリガーとして割り当てる Lambda 関数を作成します。Lambda 関数ロールに対して、KMS キーへの kms:Decrypt アクセス許可を付与します。
```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_ARN ];
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 contains the decrypted secret.
	if(event.triggerSource == 'CustomEmailSender_SignUp'){
		//Send an email message to your user via a custom provider.
		//Include the temporary password in the message.
	}
	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;
};
```

:::note warn
Lambdaイベントからもらったcodeはkmsキーを用いて復号処理しないといけない
:::

## 呼び出し権限
Amazon Cognito サービスプリンシパルに、Lambda 関数を呼び出すための cognito-idp.amazonaws.com へのアクセス権を付与します。
コンソール画面からも追加できますが、AWS CLIでも追加できます。
Lambda>関数>[function name]>アクセス権限を追加
![スクリーンショット 2023-10-12 11.01.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/6e125124-29b9-7f26-5ba1-e6a5703532d7.png)

- AWS CLI
    ```sh
    aws lambda add-permission \
      --function-name <lambda_arn> \
      --statement-id "CognitoLambdaInvokeAccess" \
      --action lambda:InvokeFunction \
      --principal cognito-idp.amazonaws.com
    ```
## Cognitoにトリガーを設定
ここは注意点あります。カスタムEメール送信者LambdaトリガーはコンソールやCloudFormationから作成できないので、AWS CLIを利用します。
```sh
# 既存の設定の確認
aws cognito-idp describe-user-pool --user-pool-id YOUR_USER_POOL_ID

# トリガー作成
aws cognito-idp update-user-pool \
  --userpool-id <userpool_id> \
  --lambda-config "CustomEmailSender={LambdaVersion=V1_0,LambdaArn= <lambda_arn> },KMSKeyID= <key_id>"　\
  --auto-verified-attributes email
```

:::note warn
AWS ドキュメントに--auto-verified-attributes emailのオプションが必要だと書かれていませんが、実行すると[Cognitoが検証と確認のためにメッセージを自動的に送信することを許可]の値が[無効]となってしまい、メール送信自体できなくなります。そのオプションを忘れずにつけてください。
:::

### おまけ
トリガーだけ削除したい時、下記のコマンドで追加したトリガーをリセットします。
```sh
aws cognito-idp update-user-pool \
  --userpool-id <userpool_id> \
  --lambda-config "{}"
```

## AWS Cognito　APIでトリガー
トリガーするAPIは下記のAWSドキュメンに書いてますが、キャプチャーを貼ります。
![スクリーンショット 2023-10-12 11.40.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2491433/2be78ef8-e9f2-512f-6279-66f46c346c39.png)

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html
# 参考
https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/user-pool-lambda-custom-email-sender.html

https://docs.aws.amazon.com/cli/latest/reference/cognito-idp/update-user-pool.html