diff --git a/packages/@aws-cdk/aws-cognito/README.md b/packages/@aws-cdk/aws-cognito/README.md index 57a442512d9f6..7bc1b4f9e9fc2 100644 --- a/packages/@aws-cdk/aws-cognito/README.md +++ b/packages/@aws-cdk/aws-cognito/README.md @@ -314,6 +314,18 @@ new cognito.UserPool(this, 'UserPool', { The default for account recovery is by phone if available and by email otherwise. A user will not be allowed to reset their password via phone if they are also using it for MFA. +#### Advanced Security Mode + +User pools can be configured to use Advanced security. You can turn the user pool advanced security features on, and customize the actions that are taken in response to different risks. Or you can use audit mode to gather metrics on detected risks without taking action. In audit mode, the advanced security features publish metrics to Amazon CloudWatch. See the [documentation on Advanced security](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-settings-advanced-security.html) to learn more. + +```ts +new cognito.UserPool(this, 'myuserpool', { + // ... + userPoolAddOnsSettings: { + advancedSecurityMode: cognito.AdvancedSecurityMode.ENFORCED, + }, +}); +``` ### Emails diff --git a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts index 59f3ce09f2057..9ac5e52b4061b 100644 --- a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts +++ b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts @@ -477,6 +477,31 @@ export interface DeviceTracking { readonly deviceOnlyRememberedOnUserPrompt: boolean; } +/** + * UserPooolAddOns settings for the user pool. + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-userpool-userpooladdons.html + */ +export interface UserPoolAddOnsSettings { + /** + * The user pool's Advanced Security Mode + * @default AdvancedSecurityMode.OFF + */ + readonly advancedSecurityMode?: AdvancedSecurityMode; +} + +/** + * The different ways in which a user pool's Advanced Security Mode can be configured. + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-userpool-userpooladdons.html#cfn-cognito-userpool-userpooladdons-advancedsecuritymode + */ +export enum AdvancedSecurityMode { + /** Enable advanced security mode */ + ENFORCED = 'ENFORCED', + /** gather metrics on detected risks without taking action. Metrics are published to Amazon CloudWatch */ + AUDIT = 'AUDIT', + /** Advanced security mode is disabled */ + OFF = 'OFF' +} + /** * Props for the UserPool construct */ @@ -650,6 +675,13 @@ export interface UserPoolProps { * @default - no key ID configured */ readonly customSenderKmsKey?: IKey; + + /** + * Define the User Pool addons settings + * + * @default - see defaults on each property of UserPoolAddOnsSettings. + */ + readonly userPoolAddOnsSettings?: UserPoolAddOnsSettings; } /** @@ -877,6 +909,9 @@ export class UserPool extends UserPoolBase { emailVerificationSubject, smsVerificationMessage, verificationMessageTemplate, + userPoolAddOns: undefinedIfNoKeys({ + advancedSecurityMode: props.userPoolAddOnsSettings?.advancedSecurityMode, + }), schema: this.schemaConfiguration(props), mfaConfiguration: props.mfa, enabledMfas: this.mfaConfiguration(props), diff --git a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-explicit-props.expected.json b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-explicit-props.expected.json index 50da9815a769b..c88464abff9ee 100644 --- a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-explicit-props.expected.json +++ b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-explicit-props.expected.json @@ -842,6 +842,9 @@ "EmailMessage": "verification email body from the integ test. Code is {####}.", "EmailSubject": "verification email subject from the integ test", "SmsMessage": "verification sms message from the integ test. Code is {####}." + }, + "UserPoolAddOns": { + "AdvancedSecurityMode": "ENFORCED" } }, "UpdateReplacePolicy": "Delete", diff --git a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-explicit-props.ts b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-explicit-props.ts index 1bc35003fa472..e3f0313f95ed7 100644 --- a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-explicit-props.ts +++ b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-explicit-props.ts @@ -1,6 +1,6 @@ import { Code, Function, IFunction, Runtime } from '@aws-cdk/aws-lambda'; import { App, CfnOutput, Duration, RemovalPolicy, Stack } from '@aws-cdk/core'; -import { BooleanAttribute, DateTimeAttribute, Mfa, NumberAttribute, StringAttribute, UserPool } from '../lib'; +import { AdvancedSecurityMode, BooleanAttribute, DateTimeAttribute, Mfa, NumberAttribute, StringAttribute, UserPool } from '../lib'; const app = new App(); const stack = new Stack(app, 'integ-user-pool'); @@ -69,6 +69,9 @@ const userpool = new UserPool(stack, 'myuserpool', { userMigration: dummyTrigger('userMigration'), verifyAuthChallengeResponse: dummyTrigger('verifyAuthChallengeResponse'), }, + userPoolAddOnsSettings: { + advancedSecurityMode: AdvancedSecurityMode.ENFORCED, + }, }); const cognitoDomain = userpool.addDomain('myuserpooldomain', { diff --git a/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts b/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts index df252d401a000..6f0d8d5f63c62 100644 --- a/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts +++ b/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts @@ -5,7 +5,7 @@ import * as lambda from '@aws-cdk/aws-lambda'; import { testDeprecated } from '@aws-cdk/cdk-build-tools'; import { CfnParameter, Duration, Stack, Tags } from '@aws-cdk/core'; import { Construct } from 'constructs'; -import { AccountRecovery, Mfa, NumberAttribute, StringAttribute, UserPool, UserPoolIdentityProvider, UserPoolOperation, VerificationEmailStyle, UserPoolEmail } from '../lib'; +import { AccountRecovery, Mfa, NumberAttribute, StringAttribute, UserPool, UserPoolIdentityProvider, UserPoolOperation, VerificationEmailStyle, UserPoolEmail, AdvancedSecurityMode } from '../lib'; describe('User Pool', () => { test('default setup', () => { @@ -1766,6 +1766,43 @@ test('device tracking is configured correctly', () => { }); }); +test.each( + [ + [AdvancedSecurityMode.ENFORCED, 'ENFORCED'], + [AdvancedSecurityMode.AUDIT, 'AUDIT'], + [AdvancedSecurityMode.OFF, 'OFF'], + ])('advanced security is configured correctly when set to (%s)', (advancedSecurityMode, compareString) => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'Pool', { + userPoolAddOnsSettings: { + advancedSecurityMode: advancedSecurityMode, + }, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Cognito::UserPool', { + UserPoolAddOns: { + AdvancedSecurityMode: compareString, + }, + }); +}); + +test('advanced security is not present if option is not provided', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'Pool', {}); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Cognito::UserPool', { + UserPoolAddOns: Match.absent(), + }); +}); + function fooFunction(scope: Construct, name: string): lambda.IFunction { return new lambda.Function(scope, name, {