Skip to content

Commit

Permalink
feat(cognito): better control sms role creation
Browse files Browse the repository at this point in the history
- Introduce a property `enableSmsRole` that can be used to override CDK
logic and explicitly enable or disable automatic creation of an IAM role
for SMS.

- Instead of creating the SMS role by default, all of the time, be smart
about determining when the role is actually needed. Create the role only
if (a) SMS is configured as MFA second factor, (b) sign in via phone
number is enabled, or (c) phone verification is required.

BREAKING CHANGE: CDK may now remove a previously created IAM role for
SMS. The role will be removed only because it's not actually required by
the user pool based on its configuration, so this should have no impact.
This behaviour can be explicitly overridden by setting `enableSmsRole`
property.

closes #6943
  • Loading branch information
Niranjan Jayakar committed Aug 7, 2020
1 parent 76a7bfd commit 01a58b6
Show file tree
Hide file tree
Showing 10 changed files with 511 additions and 487 deletions.
9 changes: 7 additions & 2 deletions packages/@aws-cdk/aws-cognito/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,13 @@ The default value is `false`.

Cognito sends various messages to its users via SMS, for different actions, ranging from account verification to
marketing. In order to send SMS messages, Cognito needs an IAM role that it can assume, with permissions that allow it
to send SMS messages. By default, CDK will create this IAM role but can also be explicily specified to an existing IAM
role using the `smsRole` property.
to send SMS messages.

By default, the CDK looks at all of the specified properties (and their defaults when not explicitly specified) and
automatically creates an SMS role, when needed. For example, if MFA second factor by SMS is enabled, the CDK will
create a new role. The `smsRole` property can be used to specify the user supplied role that should be used instead.
Additionally, the property `enableSmsRole` can be used to override the CDK's default behaviour to either enable or
suppress automatic role creation.

```ts
import { Role } from '@aws-cdk/aws-iam';
Expand Down
81 changes: 52 additions & 29 deletions packages/@aws-cdk/aws-cognito/lib/user-pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,13 @@ export interface UserPoolProps {
*/
readonly smsRoleExternalId?: string;

/**
* Setting this would explicitly enable or disable SMS role creation.
* When left unspecified, CDK will determine based on other properties if a role is needed or not.
* @default - CDK will determine based on other properties of the user pool if an SMS role should be created or not.
*/
readonly enableSmsRole?: boolean;

/**
* Methods in which a user registers or signs in to a user pool.
* Allows either username with aliases OR sign in with email, phone, or both.
Expand Down Expand Up @@ -835,41 +842,57 @@ export class UserPool extends UserPoolBase {
return { usernameAttrs, aliasAttrs, autoVerifyAttrs };
}

private smsConfiguration(props: UserPoolProps): CfnUserPool.SmsConfigurationProperty {
private smsConfiguration(props: UserPoolProps): CfnUserPool.SmsConfigurationProperty | undefined {
if (props.enableSmsRole === false && props.smsRole) {
throw new Error('enableSmsRole cannot be disabled when smsRole is specified');
}

if (props.smsRole) {
return {
snsCallerArn: props.smsRole.roleArn,
externalId: props.smsRoleExternalId,
};
} else {
const smsRoleExternalId = this.node.uniqueId.substr(0, 1223); // sts:ExternalId max length of 1224
const smsRole = props.smsRole ?? new Role(this, 'smsRole', {
assumedBy: new ServicePrincipal('cognito-idp.amazonaws.com', {
conditions: {
StringEquals: { 'sts:ExternalId': smsRoleExternalId },
},
}),
inlinePolicies: {
/*
* The UserPool is very particular that it must contain an 'sns:Publish' action as an inline policy.
* Ideally, a conditional that restricts this action to 'sms' protocol needs to be attached, but the UserPool deployment fails validation.
* Seems like a case of being excessively strict.
*/
'sns-publish': new PolicyDocument({
statements: [
new PolicyStatement({
actions: [ 'sns:Publish' ],
resources: [ '*' ],
}),
],
}),
},
});
return {
externalId: smsRoleExternalId,
snsCallerArn: smsRole.roleArn,
};
}

if (props.enableSmsRole === false) {
return undefined;
}

const mfaEnabled = props.mfa && props.mfa !== Mfa.OFF;
const mfaSms = !props.mfaSecondFactor || props.mfaSecondFactor.sms; // mfaSecondFactor.sms is true, by default if MFA is 'on'
const phoneVerification = props.signInAliases?.phone === true || props.autoVerify?.phone === true;
const roleRequired = (mfaEnabled && mfaSms) || phoneVerification;
if (!roleRequired && props.enableSmsRole === undefined) {
return undefined;
}

const smsRoleExternalId = this.node.uniqueId.substr(0, 1223); // sts:ExternalId max length of 1224
const smsRole = props.smsRole ?? new Role(this, 'smsRole', {
assumedBy: new ServicePrincipal('cognito-idp.amazonaws.com', {
conditions: {
StringEquals: { 'sts:ExternalId': smsRoleExternalId },
},
}),
inlinePolicies: {
/*
* The UserPool is very particular that it must contain an 'sns:Publish' action as an inline policy.
* Ideally, a conditional that restricts this action to 'sms' protocol needs to be attached, but the UserPool deployment fails validation.
* Seems like a case of being excessively strict.
*/
'sns-publish': new PolicyDocument({
statements: [
new PolicyStatement({
actions: [ 'sns:Publish' ],
resources: [ '*' ],
}),
],
}),
},
});
return {
externalId: smsRoleExternalId,
snsCallerArn: smsRole.roleArn,
};
}

private mfaConfiguration(props: UserPoolProps): string[] | undefined {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,65 +1,25 @@
{
"Resources": {
"myuserpoolsmsRole0E16FDD9": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "integuserpoolclientexplicitpropsmyuserpoolFC6541FF"
}
},
"Effect": "Allow",
"Principal": {
"Service": "cognito-idp.amazonaws.com"
}
}
],
"Version": "2012-10-17"
},
"Policies": [
{
"PolicyDocument": {
"Statement": [
{
"Action": "sns:Publish",
"Effect": "Allow",
"Resource": "*"
}
],
"Version": "2012-10-17"
},
"PolicyName": "sns-publish"
}
]
}
},
"myuserpool01998219": {
"Type": "AWS::Cognito::UserPool",
"Properties": {
"AccountRecoverySetting": {
"AccountRecoverySetting": {
"RecoveryMechanisms": [
{ "Name": "verified_phone_number", "Priority": 1 },
{ "Name": "verified_email", "Priority": 2 }
{
"Name": "verified_phone_number",
"Priority": 1
},
{
"Name": "verified_email",
"Priority": 2
}
]
},
"AdminCreateUserConfig": {
"AllowAdminCreateUserOnly": true
},
"EmailVerificationMessage": "The verification code to your new account is {####}",
"EmailVerificationSubject": "Verify your new account",
"SmsConfiguration": {
"ExternalId": "integuserpoolclientexplicitpropsmyuserpoolFC6541FF",
"SnsCallerArn": {
"Fn::GetAtt": [
"myuserpoolsmsRole0E16FDD9",
"Arn"
]
}
},
"SmsVerificationMessage": "The verification code to your new account is {####}",
"VerificationMessageTemplate": {
"DefaultEmailOption": "CONFIRM_WITH_CODE",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,65 +1,25 @@
{
"Resources": {
"UserPoolsmsRole4EA729DD": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "integuserpooldomaincfdistUserPool17475E8A"
}
},
"Effect": "Allow",
"Principal": {
"Service": "cognito-idp.amazonaws.com"
}
}
],
"Version": "2012-10-17"
},
"Policies": [
{
"PolicyDocument": {
"Statement": [
{
"Action": "sns:Publish",
"Effect": "Allow",
"Resource": "*"
}
],
"Version": "2012-10-17"
},
"PolicyName": "sns-publish"
}
]
}
},
"UserPool6BA7E5F2": {
"Type": "AWS::Cognito::UserPool",
"Properties": {
"AccountRecoverySetting": {
"AccountRecoverySetting": {
"RecoveryMechanisms": [
{ "Name": "verified_phone_number", "Priority": 1 },
{ "Name": "verified_email", "Priority": 2 }
{
"Name": "verified_phone_number",
"Priority": 1
},
{
"Name": "verified_email",
"Priority": 2
}
]
},
"AdminCreateUserConfig": {
"AllowAdminCreateUserOnly": true
},
"EmailVerificationMessage": "The verification code to your new account is {####}",
"EmailVerificationSubject": "Verify your new account",
"SmsConfiguration": {
"ExternalId": "integuserpooldomaincfdistUserPool17475E8A",
"SnsCallerArn": {
"Fn::GetAtt": [
"UserPoolsmsRole4EA729DD",
"Arn"
]
}
},
"SmsVerificationMessage": "The verification code to your new account is {####}",
"VerificationMessageTemplate": {
"DefaultEmailOption": "CONFIRM_WITH_CODE",
Expand Down Expand Up @@ -176,7 +136,7 @@
"Properties": {
"Code": {
"S3Bucket": {
"Ref": "AssetParametersa75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4S3BucketC6CBC09E"
"Ref": "AssetParameters8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061S3Bucket67234880"
},
"S3Key": {
"Fn::Join": [
Expand All @@ -189,7 +149,7 @@
"Fn::Split": [
"||",
{
"Ref": "AssetParametersa75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4S3VersionKeyB194AB23"
"Ref": "AssetParameters8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061S3VersionKey9802AE96"
}
]
}
Expand All @@ -202,7 +162,7 @@
"Fn::Split": [
"||",
{
"Ref": "AssetParametersa75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4S3VersionKeyB194AB23"
"Ref": "AssetParameters8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061S3VersionKey9802AE96"
}
]
}
Expand Down Expand Up @@ -244,17 +204,17 @@
}
},
"Parameters": {
"AssetParametersa75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4S3BucketC6CBC09E": {
"AssetParameters8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061S3Bucket67234880": {
"Type": "String",
"Description": "S3 bucket for asset \"a75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4\""
"Description": "S3 bucket for asset \"8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061\""
},
"AssetParametersa75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4S3VersionKeyB194AB23": {
"AssetParameters8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061S3VersionKey9802AE96": {
"Type": "String",
"Description": "S3 key for asset version \"a75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4\""
"Description": "S3 key for asset version \"8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061\""
},
"AssetParametersa75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4ArtifactHashBE5BD63C": {
"AssetParameters8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061ArtifactHash9212BF97": {
"Type": "String",
"Description": "Artifact hash for asset \"a75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4\""
"Description": "Artifact hash for asset \"8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061\""
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,65 +1,25 @@
{
"Resources": {
"UserPoolsmsRole4EA729DD": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "integuserpooldomainsigninurlUserPool1325E89F"
}
},
"Effect": "Allow",
"Principal": {
"Service": "cognito-idp.amazonaws.com"
}
}
],
"Version": "2012-10-17"
},
"Policies": [
{
"PolicyDocument": {
"Statement": [
{
"Action": "sns:Publish",
"Effect": "Allow",
"Resource": "*"
}
],
"Version": "2012-10-17"
},
"PolicyName": "sns-publish"
}
]
}
},
"UserPool6BA7E5F2": {
"Type": "AWS::Cognito::UserPool",
"Properties": {
"AccountRecoverySetting": {
"AccountRecoverySetting": {
"RecoveryMechanisms": [
{ "Name": "verified_phone_number", "Priority": 1 },
{ "Name": "verified_email", "Priority": 2 }
{
"Name": "verified_phone_number",
"Priority": 1
},
{
"Name": "verified_email",
"Priority": 2
}
]
},
"AdminCreateUserConfig": {
"AllowAdminCreateUserOnly": true
},
"EmailVerificationMessage": "The verification code to your new account is {####}",
"EmailVerificationSubject": "Verify your new account",
"SmsConfiguration": {
"ExternalId": "integuserpooldomainsigninurlUserPool1325E89F",
"SnsCallerArn": {
"Fn::GetAtt": [
"UserPoolsmsRole4EA729DD",
"Arn"
]
}
},
"SmsVerificationMessage": "The verification code to your new account is {####}",
"VerificationMessageTemplate": {
"DefaultEmailOption": "CONFIRM_WITH_CODE",
Expand Down
Loading

0 comments on commit 01a58b6

Please sign in to comment.