타사 ESP를 통해 AWS Cognito 이메일 보내기
46284 단어 cloudformationawsterraformcognito
보통 이것은 일리가 있는 것이다. 어쨌든 너는 이미 AWS 생태계에 있다.그러나 만약에 제3자 ESP(이메일 서비스 제공자), 예를 들어 Twilio Sendgrid나Mailgun/Sendinblue/Mailchimp를 사용하려면 어떻게 해야 합니까?이러한 이유 중 일부는 다음과 같습니다.
사용자 정의 메시지 Lambda 트리거 (예:
CustomMessage_ForgotPassword
가 있지만, 전자 우편 테마와 본문만 사용자 정의할 수 있고, 기본 전송을 변경할 수 없습니다.2020년 말 어느 때 AWS는 Cognito에 새로운 유형의 Lambda 트리거를 추가했다. 맞춤형 발송자인 Lambda 트리거이다.
Cognito 문서가 좀 부족합니다...코드를 복사할 수 없습니다. 명령 중의 일부 절차가 부족합니다.
또한 사용자 정의 이메일 및 SMS 발송자를 코드로 구성하는 방법도 표시되지 않습니다.
다음은 이러한 새 Cognito Lambda 트리거를 배포하고 사용하는 지침입니다.
우리는 구름 편대와 지형을 어떻게 사용해서 그것들을 배치하는지 보게 될 것이다.
사용자 지정 발송자 Lambda 트리거
더 구체적으로 말하면 두 개의 새로운 트리거가 있다.
CustomEmailSender
이메일을 보내는 기본(SES) 방법 덮어쓰기CustomSMSSender
SNS 대신 외부 서비스로 문자 메시지를 보내야 할 경우이러한 Lambda 트리거가 Cognito에서 받은 매개변수는 사용자 정의 메시지 트리거에서 가져온 매개변수와 약간 다릅니다.
CustomMessage_*
트리거를 사용할 때 코드에 기밀을 전달하지 않습니다.반대로, 보낼 전자메일이나 문자의 정확한 위치에 자리 표시자 문자열을 받을 수 있다.알림을 보내기 전에 이 자리 표시자는 Cognito에서 코드로 바뀝니다.다른 한편, 사용자 정의 송신자 트리거는 암호화된 알림 코드를 수신한다.따라서 함수 코드에서 코드를 복호화할 수 있도록 KMS 키를 설정해야 합니다.
도구는 어떻게 새 트리거를 지원합니까?
Twilio Sendgrid를 사용하여 Cognito e-메일 보내기
프레젠테이션의 경우 Cognito에 새 사용자를 등록하고 Sendgrid와 함께 이메일을 보내도록 하겠습니다.
준수 선결 조건:
코그니토를 어떻게 배치하는지에 관한 튜토리얼이 아니기 때문에 맞춤형 송신기 트리거와 관련된 부분만 살펴보겠습니다.
CloudFormation과 Terraform에 정의된 모든 자원에 대한 전체 코드를 demo repository 에서 찾을 수 있습니다.
KMS 키 만들기
위에서 말한 바와 같이, 우리는 알림 코드를 암호화하고 복호화할 키가 필요합니다.키 정책은 AWS 콘솔을 사용하여 새 KMS 키를 만들 때의 기본 권한과 일치합니다.
구름층 형성
Parameters:
...
CallingUserArn:
Description: Calling user ARN
Type: String
Resources:
...
KmsKey:
Type: AWS::KMS::Key
Properties:
Enabled: true
KeyPolicy:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
Action: 'kms:*'
Resource: '*'
- Effect: Allow
Principal:
AWS: !Ref CallingUserArn
Action:
- "kms:Create*"
- "kms:Describe*"
- "kms:Enable*"
- "kms:List*"
- "kms:Put*"
- "kms:Update*"
- "kms:Revoke*"
- "kms:Disable*"
- "kms:Get*"
- "kms:Delete*"
- "kms:TagResource"
- "kms:UntagResource"
- "kms:ScheduleKeyDeletion"
- "kms:CancelKeyDeletion"
Resource: '*'
CallingUserArn
매개변수는 IAM 사용자를 호출한 ARN을 CloudFormation으로 전달하는 팁입니다.aws cloudformation deploy ... --parameter-overrides CallingUserArn="$(aws sts get-caller-identity --query Arn --output text)"
지형.data "aws_caller_identity" "current" {}
resource "aws_kms_key" "kms_key" {
description = "KMS key for Cognito Lambda trigger"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Effect": "Allow",
"Principal": {
"AWS": "${data.aws_caller_identity.current.arn}"
},
"Action": [
"kms:Create*",
"kms:Describe*",
"kms:Enable*",
"kms:List*",
"kms:Put*",
"kms:Update*",
"kms:Revoke*",
"kms:Disable*",
"kms:Get*",
"kms:Delete*",
"kms:TagResource",
"kms:UntagResource",
"kms:ScheduleKeyDeletion",
"kms:CancelKeyDeletion"
],
"Resource": "*"
}
]
}
EOF
}
Lambda IAM 역할 만들기
표준
AWSLambdaBasicExecutionRole
관리 정책 외에, 우리는 KMS 키를 복호화하기 위해 Lambda에 접근 권한을 부여해야 한다.주의: 최소한의 권한을 가지기 위해 세립도 정책
AWSLambdaBasicExecutionRole
을 사용하길 원할 수도 있습니다.CF와 TF 스크립트의 결과는 똑같다. Lambda 트리거의 한 역할에 두 가지 전략을 추가했다.
구름층 형성
Resources:
...
LambdaTriggerRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: "sts:AssumeRole"
Principal:
Service: "lambda.amazonaws.com"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
LambdaTriggerRoleKmsPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "kms:Decrypt"
Resource: !GetAtt KmsKey.Arn
PolicyName: "LambdaKmsPolicy"
Roles:
- !Ref LambdaTriggerRole
지형.data "aws_iam_policy_document" "AWSLambdaTrustPolicy" {
version = "2012-10-17"
statement {
actions = ["sts:AssumeRole"]
effect = "Allow"
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
}
}
resource "aws_iam_role" "iam_role" {
assume_role_policy = data.aws_iam_policy_document.AWSLambdaTrustPolicy.json
name = "${var.project}-iam-role-lambda-trigger"
}
resource "aws_iam_role_policy_attachment" "iam_role_policy_attachment_lambda_basic_execution" {
role = aws_iam_role.iam_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
data "aws_iam_policy_document" "iam_policy_document_lambda_kms" {
version = "2012-10-17"
statement {
actions = ["kms:Decrypt"]
effect = "Allow"
resources = [
aws_kms_key.kms_key.arn
]
}
}
resource "aws_iam_role_policy" "iam_role_policy_lambda_kms" {
name = "${var.project}-iam-role-policy-lambda-kms"
role = aws_iam_role.iam_role.name
policy = data.aws_iam_policy_document.iam_policy_document_lambda_kms.json
}
Lambda 함수 만들기(Node.js 코드)
AWS SDK 자체와 달리
@aws-crypto/client-node
암호화 라이브러리는 코드와 함께 패키지화하고 배포해야 합니다.노드가 없으면js는 로컬에 설치되어 있으며, Docker를 사용하여 의존항을 설치할 수 있습니다.클론 재구매 중:cd lambda/
docker run -it --rm -v $(pwd):/var/app node:12 bash
npm i
기능 코드:const AWS = require('aws-sdk')
const b64 = require('base64-js')
const encryptionSdk = require('@aws-crypto/client-node')
const sgMail = require("@sendgrid/mail")
const { decrypt } = encryptionSdk.buildClient(encryptionSdk.CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT)
const keyIds = [process.env.KEY_ID];
const keyring = new encryptionSdk.KmsKeyringNode({ keyIds })
sgMail.setApiKey(process.env.SENDGRID_API_KEY)
exports.handler = async(event) => {
let plainTextCode
if (event.request.code) {
const { plaintext, messageHeader } = await decrypt(keyring, b64.toByteArray(event.request.code))
plainTextCode = plaintext
}
const msg = {
to: event.request.userAttributes.email,
from: "[email protected]",
subject: "Your Cognito code",
text: `Your code: ${plainTextCode.toString()}`,
}
await sgMail.send(msg)
}
KMS 키 ARN 및 ESP API 키를 환경 변수로 전달합니다.알림 코드는 암호화되어 전자 메일 공급업체 API에 보내는 메시지 본문에서 사용할 수 있습니다.함수에 전달된 객체 예제
event
:{
"version": "1",
"triggerSource": "CustomEmailSender_ForgotPassword",
"region": "us-east-1",
"userPoolId": "us-east-1_LnS...",
"userName": "54cf7eb7-0b96-4304-...",
"callerContext": {
"awsSdkVersion": "aws-sdk-nodejs-2.856.0",
"clientId": "6u7c9vr3pkstoog..."
},
"request": {
"type": "customEmailSenderRequestV1",
"code": "AYADeILxywKhhaq8Ys4mh0aHutYAgQACABVhd3MtY3J5c...",
"clientMetadata": null,
"userAttributes": {
"sub": "54cf7eb7-0b96-4304-8d6b-...",
"email_verified": "true",
"cognito:user_status": "CONFIRMED",
"cognito:email_alias": "[email protected]",
"phone_number_verified": "false",
"phone_number": "...",
"given_name": "Max",
"family_name": "Ivanov",
"email": "[email protected]"
}
}
}
Lambda 함수 생성(infra)
우리는 노드를 정의할 것이다.Cognito는 이메일을 보낼 때마다 js Lambda를 트리거합니다.ZIP 파일로 배치됩니다.사용자 정의 발송자 트리거의 유일한 부분은 환경 변수입니다.
KMS 키 ID에 사용할 변수와 e-메일 공급업체 API 키가 필요합니다.
구름층 형성
Parameters:
...
SendgridApiKey:
Description: Sendgrid API key
Type: String
Resources:
...
LambdaTrigger:
Type: AWS::Lambda::Function
Properties:
Code: "../lambda"
Environment:
Variables:
KEY_ID: !GetAtt KmsKey.Arn
SENDGRID_API_KEY: !Ref SendgridApiKey
FunctionName: !Sub ${ProjectName}-lambda-custom-email-sender-trigger
PackageType: Zip
Role: !GetAtt LambdaTriggerRole.Arn
Runtime: nodejs12.x
Handler: index.handler
만약 네가 구름의 형성에 익숙하다면 어떤 의외의 일도 없을 것이다.지형.
data "archive_file" "lambda" {
type = "zip"
source_dir = "../lambda"
output_path = "lambda.zip"
}
resource "aws_lambda_function" "lambda_function_trigger" {
environment {
variables = {
KEY_ID = aws_kms_key.kms_key.arn
SENDGRID_API_KEY = var.sendgrid_api_key
}
}
code_signing_config_arn = ""
description = ""
filename = data.archive_file.lambda.output_path
function_name = "${var.project}-lambda-function-trigger"
role = aws_iam_role.iam_role.arn
handler = "index.handler"
runtime = "nodejs12.x"
source_code_hash = filebase64sha256(data.archive_file.lambda.output_path)
}
source_code_hash
기능 코드를 수정할 때마다 자원은 변경된 것으로 표시되고 코드는 재배치됩니다.Cognito 사용자 풀 만들기
이것은 가장 사람을 곤혹스럽게 하는 부분이다.사용자 풀의
LambdaConfig
설정이 필요합니다.Cognito 호출된 Lambda 트리거 구성을 저장하는 객체입니다.우리가 관심 있는 두 가지 새로운 옵션은 다음과 같습니다.CustomEmailSender: { LambdaArn: "...", LambdaVersion: "..." }
KMSKeyID: "..."
구름층 형성
공식 문서에 따르면 사용자 정의 발송자 설정 옵션은 Not currently supported by AWS CloudFormation이라고 한다.그러나 다행히도 사실은 그렇지 않았다.그것은 내가 운행하는 여러 테스트에서 매우 효과적이다.
Resources:
UserPool:
Type: AWS::Cognito::UserPool
Properties:
AccountRecoverySetting:
RecoveryMechanisms:
- Name: verified_email
Priority: 1
AutoVerifiedAttributes:
- email
LambdaConfig:
CustomEmailSender:
LambdaArn: !GetAtt LambdaTrigger.Arn
LambdaVersion: "V1_0"
KMSKeyID: !GetAtt KmsKey.Arn
UsernameConfiguration:
CaseSensitive: false
UserPoolName: !Sub ${ProjectName}-user-pool
UsernameAttributes:
- email
Policies:
PasswordPolicy:
MinimumLength: 10
Schema:
- Name: name
AttributeDataType: String
Mutable: true
Required: true
- Name: email
AttributeDataType: String
Mutable: true
Required: true
LambdaConfig:
는 위에서 가장 흥미를 느끼는 부분이다.지형.
지형의 특징 상태를 추적하기 위해 open issue가 있다.그러나 그것이 사용되기 전에 우리가 지금 무엇을 할 수 있을까?
null_resource
이 목표를 실현하는 데 도움이 되지만 여전히 문제가 존재한다.CloudFormation 정의에 대한 링크를 해제하면 리소스 정의에
CustomEmailSender
와 관련된 내용이 추가되지 않으므로 Terraform의 좋은 이전 Cognito 사용자 풀입니다.resource "aws_cognito_user_pool" "cognito_user_pool" {
name = "${var.project}-cognito-user-pool"
account_recovery_setting {
recovery_mechanism {
name = "verified_email"
priority = 1
}
}
auto_verified_attributes = ["email"]
password_policy {
minimum_length = 10
temporary_password_validity_days = 7
require_lowercase = false
require_numbers = false
require_symbols = false
require_uppercase = false
}
schema {
attribute_data_type = "String"
developer_only_attribute = false
mutable = true
name = "email"
required = true
string_attribute_constraints {
max_length = "2048"
min_length = "0"
}
}
schema {
attribute_data_type = "String"
developer_only_attribute = false
mutable = true
name = "name"
required = true
string_attribute_constraints {
max_length = "2048"
min_length = "0"
}
}
username_attributes = ["email"]
username_configuration {
case_sensitive = false
}
}
사용자 풀에서 Lambda 구성을 설정하려면 aws cognito-idp update-user-pool --lambda-config "CustomEmailSender={LambdaVersion=V1_0,LambdaArn=...
AWS CLI 명령을 사용합니다.문제는 이 명령에 다른 모든 관련 풀 옵션을 전달하지 않으면 기본값으로 재설정된다는 것이다.제안된 솔루션:
1. lambda 구성을 설정하지 않고 Terraform 스택을 배포합니다.
2.
update-user-pool
명령에 예상되는 입력 변수의 프레임을 생성합니다.aws cognito-idp update-user-pool --user-pool-id us-east-1_evzTb... --generate-cli-skeleton input
3. 사용자 풀의 현재 구성을 가져오려면:aws cognito-idp describe-user-pool --user-pool-id us-east-1_evzTb... --query UserPool > input.json
4. 가져온 구성에서 뼈대에 나열되지 않은 키를 제거합니다.승인된 구성 옵션은 그대로 유지update-user-pool
할 수 있습니다.누군가가 이 일을 자동으로 완성하기 위해 스크립트를 생각해 낼 수도 있다...하지만 나는 수작업으로 편집했다.5. Terraform을 사용하여 새
null_resource
를 추가하고 배치합니다.locals {
update_user_pool_command = "aws cognito-idp update-user-pool --user-pool-id ${aws_cognito_user_pool.cognito_user_pool.id} --cli-input-json file://${var.update_user_pool_config_file} --lambda-config \"CustomEmailSender={LambdaVersion=V1_0,LambdaArn=${aws_lambda_function.lambda_function_trigger.arn}},KMSKeyID=${aws_kms_key.kms_key.arn}\""
}
resource "null_resource" "cognito_user_pool_lambda_config" {
provisioner "local-exec" {
command = local.update_user_pool_command
}
depends_on = [local.update_user_pool_command]
triggers = {
input_json = filemd5(var.update_user_pool_config_file)
update_user_pool_command = local.update_user_pool_command
}
}
여기서 무슨 일이 일어났는지 빠르게 평론해 보세요.우리는 update-user-pool
명령을 사용하여 국부 값을 정의합니다.이것은 사용자 풀 ID, 단계 4.
에서 준비한 현재 사용자 풀 설정이 있는 JSON 파일과 lambda 설정을 받아들인다.Terraformnull
리소스는 처음 실행할 때apply
와 명령이나 프로필을 업데이트할 때마다 이 명령을 실행합니다.오류 해석 매개 변수'cli input json'을 받은 경우 잘못된 json을 받았습니다.오류, 인자 json을 입력한 경로가 정확하고 접두사가
file://
인지 확인하십시오.즉--cli-input-json file://${var.update_user_pool_config_file}
.매개변수 유효성 검사 실패: 입력에서 알 수 없는 매개변수: "Id",..."오류, 파라미터 파일에서
update-user-pool
지원하지 않는 모든 키가 삭제되었는지 확인하십시오.UpdateUserPool 작업을 호출할 때 오류(Invalid Parameter Exception)가 발생하면 PasswordPolicy에서 UnusesaccountValidity Days 대신 임시 PasswordValidity Days 오류를 사용하십시오
AdminCreateUserConfig.UnusedAccountValidityDays
설정을 삭제하십시오.로 대체Policies.PasswordPolicy.TemporaryPasswordValidityDays
.유효성 보장
모든 리소스가 배포되면 ESP에서 코드가 있는 이메일을 보낼 수 있도록 새 사용자를 등록할 수 있습니다.
CloudFormation을 사용하면
aws cloudformation describe-stacks --stack-name cognito-custom-email-sender-cf-stack --query "Stacks[0].Outputs"
Terraform의 경우 출력에 나열됩니다.새 사용자를 등록하려면 다음과 같이 하십시오.
aws cognito-idp sign-up --client-id <CLIENT_ID> --username [email protected] --password <PASSOWORD> --user-attributes Name="name",Value="Max Ivanov"
{
"UserConfirmed": false,
"CodeDeliveryDetails": {
"Destination": "h***@m***.io",
"DeliveryMedium": "EMAIL",
"AttributeName": "email"
},
"UserSub": "51c9045e-2f3e-4..."
}
성공했어!색인을 얻으면프로세서가 CloudWatch에서 오류를 정의하거나 내보내지 않았습니다. 포함된 폴더를 압축하는 대신 함수 파일만 압축해야 합니다.
KMS 키를 얻으려면 arn이 문자열이어야 합니다.또는 데이터 키를 복호화할 수 없으며 하나 이상의 KMS CMK 오류가 발생했습니다.CloudWatch에서 오류가 발생했습니다. 환경 변수의 KMS 키를 Lambda에 전달하고, 이 값은 KMS 키의 ARN이지 ID가 아닙니다.
깨끗이 정리하다
적어도 자원을 없애는 것은 훨씬 쉽다!
구름층 형성
aws cloudformation delete-stack --stack-name cognito-custom-email-sender-cf-stack
지형.terraform destroy
도구책
...
이제 Cognito에서 이메일 및 문자 알림을 보내는 방법은 제한되지 않습니다.프로젝트에 더 적합한 알림 서비스/공급자를 사용하십시오.
만약 당신이 이런 내용을 좋아한다면 트위터에서 최신 업데이트를 얻을 수 있습니다.
Reference
이 문제에 관하여(타사 ESP를 통해 AWS Cognito 이메일 보내기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/maxivanov/send-aws-cognito-emails-with-3rd-party-esps-3cok텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)