1
- import * as iam from '@aws-cdk/aws-iam' ;
1
+ import { IRole , PolicyDocument , PolicyStatement , Role , ServicePrincipal } from '@aws-cdk/aws-iam' ;
2
2
import * as lambda from '@aws-cdk/aws-lambda' ;
3
3
import { Construct , IResource , Lazy , Resource } from '@aws-cdk/core' ;
4
4
import { CfnUserPool } from './cognito.generated' ;
@@ -212,6 +212,78 @@ export interface UserPoolTriggers {
212
212
[ trigger : string ] : lambda . IFunction | undefined ;
213
213
}
214
214
215
+ /**
216
+ * The email verification style
217
+ */
218
+ export enum VerificationEmailStyle {
219
+ /** Verify email via code */
220
+ CODE = 'CONFIRM_WITH_CODE' ,
221
+ /** Verify email via link */
222
+ LINK = 'CONFIRM_WITH_LINK' ,
223
+ }
224
+
225
+ /**
226
+ * User pool configuration for user self sign up.
227
+ */
228
+ export interface UserVerificationConfig {
229
+ /**
230
+ * The email subject template for the verification email sent to the user upon sign up.
231
+ * See https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-settings-message-templates.html to
232
+ * learn more about message templates.
233
+ * @default 'Verify your new account'
234
+ */
235
+ readonly emailSubject ?: string ;
236
+
237
+ /**
238
+ * The email body template for the verification email sent to the user upon sign up.
239
+ * See https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-settings-message-templates.html to
240
+ * learn more about message templates.
241
+ * @default 'Hello {username}, Your verification code is {####}'
242
+ */
243
+ readonly emailBody ?: string ;
244
+
245
+ /**
246
+ * Emails can be verified either using a code or a link.
247
+ * Learn more at https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-settings-email-verification-message-customization.html
248
+ * @default VerificationEmailStyle.CODE
249
+ */
250
+ readonly emailStyle ?: VerificationEmailStyle ;
251
+
252
+ /**
253
+ * The message template for the verification SMS sent to the user upon sign up.
254
+ * See https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-settings-message-templates.html to
255
+ * learn more about message templates.
256
+ * @default 'The verification code to your new account is {####}'
257
+ */
258
+ readonly smsMessage ?: string ;
259
+ }
260
+
261
+ /**
262
+ * User pool configuration when administrators sign users up.
263
+ */
264
+ export interface UserInvitationConfig {
265
+ /**
266
+ * The template to the email subject that is sent to the user when an administrator signs them up to the user pool.
267
+ * @default 'Your temporary password'
268
+ */
269
+ readonly emailSubject ?: string ;
270
+
271
+ /**
272
+ * The template to the email body that is sent to the user when an administrator signs them up to the user pool.
273
+ * @default 'Your username is {username} and temporary password is {####}.'
274
+ */
275
+ readonly emailBody ?: string ;
276
+
277
+ /**
278
+ * The template to the SMS message that is sent to the user when an administrator signs them up to the user pool.
279
+ * @default 'Your username is {username} and temporary password is {####}'
280
+ */
281
+ readonly smsMessage ?: string ;
282
+ }
283
+
284
+ /**
285
+ * Props for the UserPool construct
286
+ */
215
287
export interface UserPoolProps {
216
288
/**
217
289
* Name of the user pool
@@ -220,6 +292,40 @@ export interface UserPoolProps {
220
292
*/
221
293
readonly userPoolName ?: string ;
222
294
295
+ /**
296
+ * Whether self sign up should be enabled. This can be further configured via the `selfSignUp` property.
297
+ * @default false
298
+ */
299
+ readonly selfSignUpEnabled ?: boolean ;
300
+
301
+ /**
302
+ * Configuration around users signing themselves up to the user pool.
303
+ * Enable or disable self sign-up via the `selfSignUpEnabled` property.
304
+ * @default - see defaults in UserVerificationConfig
305
+ */
306
+ readonly userVerification ?: UserVerificationConfig ;
307
+
308
+ /**
309
+ * Configuration around admins signing up users into a user pool.
310
+ * @default - see defaults in UserInvitationConfig
311
+ */
312
+ readonly userInvitation ?: UserInvitationConfig ;
313
+
314
+ /**
315
+ * The IAM role that Cognito will assume while sending SMS messages.
316
+ * @default - a new IAM role is created
317
+ */
318
+ readonly smsRole ?: IRole ;
319
+
320
+ /**
321
+ * The 'ExternalId' that Cognito service must using when assuming the `smsRole`, if the role is restricted with an 'sts:ExternalId' conditional.
322
+ * Learn more about ExternalId here - https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html
323
+ *
324
+ * This property will be ignored if `smsRole` is not specified.
325
+ * @default - No external id will be configured
326
+ */
327
+ readonly smsRoleExternalId ?: string ;
328
+
223
329
/**
224
330
* Method used for user registration & sign in.
225
331
* Allows either username with aliases OR sign in with email, phone, or both.
@@ -400,12 +506,47 @@ export class UserPool extends Resource implements IUserPool {
400
506
}
401
507
}
402
508
509
+ const emailVerificationSubject = props . userVerification ?. emailSubject ?? 'Verify your new account' ;
510
+ const emailVerificationMessage = props . userVerification ?. emailBody ?? 'Hello {username}, Your verification code is {####}' ;
511
+ const smsVerificationMessage = props . userVerification ?. smsMessage ?? 'The verification code to your new account is {####}' ;
512
+
513
+ const defaultEmailOption = props . userVerification ?. emailStyle ?? VerificationEmailStyle . CODE ;
514
+ const verificationMessageTemplate : CfnUserPool . VerificationMessageTemplateProperty =
515
+ ( defaultEmailOption === VerificationEmailStyle . CODE ) ? {
516
+ defaultEmailOption,
517
+ emailMessage : emailVerificationMessage ,
518
+ emailSubject : emailVerificationSubject ,
519
+ smsMessage : smsVerificationMessage ,
520
+ } : {
521
+ defaultEmailOption,
522
+ emailMessageByLink : emailVerificationMessage ,
523
+ emailSubjectByLink : emailVerificationSubject ,
524
+ smsMessage : smsVerificationMessage
525
+ } ;
526
+
527
+ const inviteMessageTemplate : CfnUserPool . InviteMessageTemplateProperty = {
528
+ emailMessage : props . userInvitation ?. emailBody ,
529
+ emailSubject : props . userInvitation ?. emailSubject ,
530
+ smsMessage : props . userInvitation ?. smsMessage ,
531
+ } ;
532
+ const selfSignUpEnabled = props . selfSignUpEnabled !== undefined ? props . selfSignUpEnabled : false ;
533
+ const adminCreateUserConfig : CfnUserPool . AdminCreateUserConfigProperty = {
534
+ allowAdminCreateUserOnly : ! selfSignUpEnabled ,
535
+ inviteMessageTemplate : props . userInvitation !== undefined ? inviteMessageTemplate : undefined ,
536
+ } ;
537
+
403
538
const userPool = new CfnUserPool ( this , 'Resource' , {
404
539
userPoolName : props . userPoolName ,
405
540
usernameAttributes,
406
541
aliasAttributes,
407
542
autoVerifiedAttributes : props . autoVerifiedAttributes ,
408
- lambdaConfig : Lazy . anyValue ( { produce : ( ) => this . triggers } )
543
+ lambdaConfig : Lazy . anyValue ( { produce : ( ) => this . triggers } ) ,
544
+ smsConfiguration : this . smsConfiguration ( props ) ,
545
+ adminCreateUserConfig,
546
+ emailVerificationMessage,
547
+ emailVerificationSubject,
548
+ smsVerificationMessage,
549
+ verificationMessageTemplate,
409
550
} ) ;
410
551
411
552
this . userPoolId = userPool . ref ;
@@ -528,8 +669,45 @@ export class UserPool extends Resource implements IUserPool {
528
669
private addLambdaPermission ( fn : lambda . IFunction , name : string ) : void {
529
670
const normalize = name . charAt ( 0 ) . toUpperCase ( ) + name . slice ( 1 ) ;
530
671
fn . addPermission ( `${ normalize } Cognito` , {
531
- principal : new iam . ServicePrincipal ( 'cognito-idp.amazonaws.com' ) ,
672
+ principal : new ServicePrincipal ( 'cognito-idp.amazonaws.com' ) ,
532
673
sourceArn : this . userPoolArn
533
674
} ) ;
534
675
}
676
+
677
+ private smsConfiguration ( props : UserPoolProps ) : CfnUserPool . SmsConfigurationProperty {
678
+ if ( props . smsRole ) {
679
+ return {
680
+ snsCallerArn : props . smsRole . roleArn ,
681
+ externalId : props . smsRoleExternalId
682
+ } ;
683
+ } else {
684
+ const smsRoleExternalId = this . node . uniqueId . substr ( 0 , 1223 ) ; // sts:ExternalId max length of 1224
685
+ const smsRole = props . smsRole ?? new Role ( this , 'smsRole' , {
686
+ assumedBy : new ServicePrincipal ( 'cognito-idp.amazonaws.com' , {
687
+ conditions : {
688
+ StringEquals : { 'sts:ExternalId' : smsRoleExternalId }
689
+ }
690
+ } ) ,
691
+ inlinePolicies : {
692
+ /*
693
+ * The UserPool is very particular that it must contain an 'sns:Publish' action as an inline policy.
694
+ * Ideally, a conditional that restricts this action to 'sms' protocol needs to be attached, but the UserPool deployment fails validation.
695
+ * Seems like a case of being excessively strict.
696
+ */
697
+ 'sns-publish' : new PolicyDocument ( {
698
+ statements : [
699
+ new PolicyStatement ( {
700
+ actions : [ 'sns:Publish' ] ,
701
+ resources : [ '*' ] ,
702
+ } )
703
+ ]
704
+ } )
705
+ }
706
+ } ) ;
707
+ return {
708
+ externalId : smsRoleExternalId ,
709
+ snsCallerArn : smsRole . roleArn
710
+ } ;
711
+ }
712
+ }
535
713
}
0 commit comments