forked from aws/aws-cdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cognito): user pool: send emails using Amazon SES (aws#17117)
add support for SES integration by introducing a new property for configuring email settings for a user pool. This feature supports both types of integration with SES. 1. Using the COGNITO_DEFAULT sending account, but providing a custom email address 2. Using the DEVELOPER sending account This feature does not automate any configuration on SES since that is not currently possible with CloudFormation and requires a manual verification step. To use the SES integration introduced in this feature the user will have had to already configured a verified email address in Amazon SES and followed the steps outlined here: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-email.html closes aws#6768 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
- Loading branch information
1 parent
610215d
commit 977b10f
Showing
5 changed files
with
539 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
import { Stack, Token } from '@aws-cdk/core'; | ||
import { Construct } from 'constructs'; | ||
import { toASCII as punycodeEncode } from 'punycode/'; | ||
|
||
/** | ||
* The valid Amazon SES configuration regions | ||
*/ | ||
const REGIONS = ['us-east-1', 'us-west-2', 'eu-west-1']; | ||
|
||
/** | ||
* Configuration for Cognito sending emails via Amazon SES | ||
*/ | ||
export interface UserPoolSESOptions { | ||
/** | ||
* The verified Amazon SES email address that Cognito should | ||
* use to send emails. | ||
* | ||
* The email address used must be a verified email address | ||
* in Amazon SES and must be configured to allow Cognito to | ||
* send emails. | ||
* | ||
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-email.html | ||
*/ | ||
readonly fromEmail: string; | ||
|
||
/** | ||
* An optional name that should be used as the sender's name | ||
* along with the email. | ||
* | ||
* @default - no name | ||
*/ | ||
readonly fromName?: string; | ||
|
||
/** | ||
* The destination to which the receiver of the email should reploy to. | ||
* | ||
* @default - same as the fromEmail | ||
*/ | ||
readonly replyTo?: string; | ||
|
||
/** | ||
* The name of a configuration set in Amazon SES that should | ||
* be applied to emails sent via Cognito. | ||
* | ||
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-userpool-emailconfiguration.html#cfn-cognito-userpool-emailconfiguration-configurationset | ||
* | ||
* @default - no configuration set | ||
*/ | ||
readonly configurationSetName?: string; | ||
|
||
/** | ||
* Required if the UserPool region is different than the SES region. | ||
* | ||
* If sending emails with a Amazon SES verified email address, | ||
* and the region that SES is configured is different than the | ||
* region in which the UserPool is deployed, you must specify that | ||
* region here. | ||
* | ||
* Must be 'us-east-1', 'us-west-2', or 'eu-west-1' | ||
* | ||
* @default - The same region as the Cognito UserPool | ||
*/ | ||
readonly sesRegion?: string; | ||
} | ||
|
||
/** | ||
* Result of binding email settings with a user pool | ||
*/ | ||
interface UserPoolEmailConfig { | ||
/** | ||
* The name of the configuration set in SES. | ||
* | ||
* @default - none | ||
*/ | ||
readonly configurationSet?: string; | ||
|
||
/** | ||
* Specifies whether to use Cognito's built in email functionality | ||
* or SES. | ||
* | ||
* @default - Cognito built in email functionality | ||
*/ | ||
readonly emailSendingAccount?: string; | ||
|
||
/** | ||
* Identifies either the sender's email address or the sender's | ||
* name with their email address. | ||
* | ||
* If emailSendingAccount is DEVELOPER then this cannot be specified. | ||
* | ||
* @default 'no-reply@verificationemail.com' | ||
*/ | ||
readonly from?: string; | ||
|
||
/** | ||
* The destination to which the receiver of the email should reply to. | ||
* | ||
* @default - same as `from` | ||
*/ | ||
readonly replyToEmailAddress?: string; | ||
|
||
/** | ||
* The ARN of a verified email address in Amazon SES. | ||
* | ||
* required if emailSendingAccount is DEVELOPER or if | ||
* 'from' is provided. | ||
* | ||
* @default - none | ||
*/ | ||
readonly sourceArn?: string; | ||
} | ||
|
||
/** | ||
* Configure how Cognito sends emails | ||
*/ | ||
export abstract class UserPoolEmail { | ||
/** | ||
* Send email using Cognito | ||
*/ | ||
public static withCognito(replyTo?: string): UserPoolEmail { | ||
return new CognitoEmail(replyTo); | ||
} | ||
|
||
/** | ||
* Send email using SES | ||
*/ | ||
public static withSES(options: UserPoolSESOptions): UserPoolEmail { | ||
return new SESEmail(options); | ||
} | ||
|
||
|
||
/** | ||
* Returns the email configuration for a Cognito UserPool | ||
* that controls how Cognito will send emails | ||
* @internal | ||
*/ | ||
public abstract _bind(scope: Construct): UserPoolEmailConfig; | ||
|
||
} | ||
|
||
class CognitoEmail extends UserPoolEmail { | ||
constructor(private readonly replyTo?: string) { | ||
super(); | ||
} | ||
|
||
public _bind(_scope: Construct): UserPoolEmailConfig { | ||
return { | ||
replyToEmailAddress: encodeAndTest(this.replyTo), | ||
emailSendingAccount: 'COGNITO_DEFAULT', | ||
}; | ||
|
||
} | ||
} | ||
|
||
class SESEmail extends UserPoolEmail { | ||
constructor(private readonly options: UserPoolSESOptions) { | ||
super(); | ||
} | ||
|
||
public _bind(scope: Construct): UserPoolEmailConfig { | ||
const region = Stack.of(scope).region; | ||
|
||
if (Token.isUnresolved(region) && !this.options.sesRegion) { | ||
throw new Error('Your stack region cannot be determined so "sesRegion" is required in SESOptions'); | ||
} | ||
|
||
if (this.options.sesRegion && !REGIONS.includes(this.options.sesRegion)) { | ||
throw new Error(`sesRegion must be one of 'us-east-1', 'us-west-2', 'eu-west-1'. received ${this.options.sesRegion}`); | ||
} else if (!this.options.sesRegion && !REGIONS.includes(region)) { | ||
throw new Error(`Your stack is in ${region}, which is not a SES Region. Please provide a valid value for 'sesRegion'`); | ||
} | ||
|
||
let from = this.options.fromEmail; | ||
if (this.options.fromName) { | ||
from = `${this.options.fromName} <${this.options.fromEmail}>`; | ||
} | ||
|
||
return { | ||
from: encodeAndTest(from), | ||
replyToEmailAddress: encodeAndTest(this.options.replyTo), | ||
configurationSet: this.options.configurationSetName, | ||
emailSendingAccount: 'DEVELOPER', | ||
sourceArn: Stack.of(scope).formatArn({ | ||
service: 'ses', | ||
resource: 'identity', | ||
resourceName: encodeAndTest(this.options.fromEmail), | ||
region: this.options.sesRegion ?? region, | ||
}), | ||
}; | ||
} | ||
} | ||
|
||
function encodeAndTest(input: string | undefined): string | undefined { | ||
if (input) { | ||
const local = input.split('@')[0]; | ||
if (!/[\p{ASCII}]+/u.test(local)) { | ||
throw new Error('the local part of the email address must use ASCII characters only'); | ||
} | ||
return punycodeEncode(input); | ||
} else { | ||
return undefined; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.