Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cognito): better control sms role creation #9513

Merged
merged 9 commits into from
Aug 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 0 additions & 46 deletions packages/@aws-cdk/aws-appsync/test/integ.graphql-iam.expected.json
Original file line number Diff line number Diff line change
@@ -1,42 +1,5 @@
{
"Resources": {
"PoolsmsRoleC3352CE6": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "awsappsyncintegPool5D14B05B"
}
},
"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"
}
]
}
},
"PoolD3F588B8": {
"Type": "AWS::Cognito::UserPool",
"Properties": {
Expand All @@ -57,15 +20,6 @@
},
"EmailVerificationMessage": "The verification code to your new account is {####}",
"EmailVerificationSubject": "Verify your new account",
"SmsConfiguration": {
"ExternalId": "awsappsyncintegPool5D14B05B",
"SnsCallerArn": {
"Fn::GetAtt": [
"PoolsmsRoleC3352CE6",
"Arn"
]
}
},
"SmsVerificationMessage": "The verification code to your new account is {####}",
"UserPoolName": "myPool",
"VerificationMessageTemplate": {
Expand Down
46 changes: 0 additions & 46 deletions packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json
Original file line number Diff line number Diff line change
@@ -1,42 +1,5 @@
{
"Resources": {
"PoolsmsRoleC3352CE6": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "awsappsyncintegPool5D14B05B"
}
},
"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"
}
]
}
},
"PoolD3F588B8": {
"Type": "AWS::Cognito::UserPool",
"Properties": {
Expand All @@ -57,15 +20,6 @@
},
"EmailVerificationMessage": "The verification code to your new account is {####}",
"EmailVerificationSubject": "Verify your new account",
"SmsConfiguration": {
"ExternalId": "awsappsyncintegPool5D14B05B",
"SnsCallerArn": {
"Fn::GetAtt": [
"PoolsmsRoleC3352CE6",
"Arn"
]
}
},
"SmsVerificationMessage": "The verification code to your new account is {####}",
"UserPoolName": "myPool",
"VerificationMessageTemplate": {
Expand Down
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
80 changes: 51 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,56 @@ 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.construct.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 mfaConfiguration = this.mfaConfiguration(props);
const phoneVerification = props.signInAliases?.phone === true || props.autoVerify?.phone === true;
const roleRequired = mfaConfiguration?.includes('SMS_MFA') || phoneVerification;
if (!roleRequired && props.enableSmsRole === undefined) {
return undefined;
}

const smsRoleExternalId = this.construct.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
Loading