diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/integ-user-pool-client-default-redirect-uri.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/integ-user-pool-client-default-redirect-uri.assets.json new file mode 100644 index 0000000000000..7fe79387a177d --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/integ-user-pool-client-default-redirect-uri.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "c7dbdbe9f15846d8e11fb25d3425ce2563a3c3959793b5097b28cadf936bad6b": { + "source": { + "path": "integ-user-pool-client-default-redirect-uri.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "c7dbdbe9f15846d8e11fb25d3425ce2563a3c3959793b5097b28cadf936bad6b.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/integ-user-pool-client-default-redirect-uri.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/integ-user-pool-client-default-redirect-uri.template.json new file mode 100644 index 0000000000000..8c908db4568e3 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/integ-user-pool-client-default-redirect-uri.template.json @@ -0,0 +1,98 @@ +{ + "Resources": { + "myuserpool01998219": { + "Type": "AWS::Cognito::UserPool", + "Properties": { + "AccountRecoverySetting": { + "RecoveryMechanisms": [ + { + "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", + "SmsVerificationMessage": "The verification code to your new account is {####}", + "VerificationMessageTemplate": { + "DefaultEmailOption": "CONFIRM_WITH_CODE", + "EmailMessage": "The verification code to your new account is {####}", + "EmailSubject": "Verify your new account", + "SmsMessage": "The verification code to your new account is {####}" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + }, + "myuserpoolmyuserpoolclientAFB2274E": { + "Type": "AWS::Cognito::UserPoolClient", + "Properties": { + "AllowedOAuthFlows": [ + "implicit", + "code" + ], + "AllowedOAuthFlowsUserPoolClient": true, + "AllowedOAuthScopes": [ + "profile", + "phone", + "email", + "openid", + "aws.cognito.signin.user.admin" + ], + "CallbackURLs": [ + "https://aaa.example.com", + "https://bbb.example.com", + "https://ccc.example.com" + ], + "DefaultRedirectURI": "https://aaa.example.com", + "SupportedIdentityProviders": [ + "COGNITO" + ], + "UserPoolId": { + "Ref": "myuserpool01998219" + } + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/integ.json new file mode 100644 index 0000000000000..23cd1d353f6a1 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "integ-user-pool-client-default-redirect-uri-test/DefaultTest": { + "stacks": [ + "integ-user-pool-client-default-redirect-uri" + ], + "assertionStack": "integ-user-pool-client-default-redirect-uri-test/DefaultTest/DeployAssert", + "assertionStackName": "integuserpoolclientdefaultredirecturitestDefaultTestDeployAssertF79A1D23" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/integuserpoolclientdefaultredirecturitestDefaultTestDeployAssertF79A1D23.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/integuserpoolclientdefaultredirecturitestDefaultTestDeployAssertF79A1D23.assets.json new file mode 100644 index 0000000000000..12ed315aa9346 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/integuserpoolclientdefaultredirecturitestDefaultTestDeployAssertF79A1D23.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "integuserpoolclientdefaultredirecturitestDefaultTestDeployAssertF79A1D23.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/integuserpoolclientdefaultredirecturitestDefaultTestDeployAssertF79A1D23.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/integuserpoolclientdefaultredirecturitestDefaultTestDeployAssertF79A1D23.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/integuserpoolclientdefaultredirecturitestDefaultTestDeployAssertF79A1D23.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/manifest.json new file mode 100644 index 0000000000000..0d6d6c7e6c9a3 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/manifest.json @@ -0,0 +1,119 @@ +{ + "version": "36.0.0", + "artifacts": { + "integ-user-pool-client-default-redirect-uri.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integ-user-pool-client-default-redirect-uri.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integ-user-pool-client-default-redirect-uri": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integ-user-pool-client-default-redirect-uri.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/c7dbdbe9f15846d8e11fb25d3425ce2563a3c3959793b5097b28cadf936bad6b.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integ-user-pool-client-default-redirect-uri.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "integ-user-pool-client-default-redirect-uri.assets" + ], + "metadata": { + "/integ-user-pool-client-default-redirect-uri/myuserpool/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "myuserpool01998219" + } + ], + "/integ-user-pool-client-default-redirect-uri/myuserpool/myuserpoolclient/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "myuserpoolmyuserpoolclientAFB2274E" + } + ], + "/integ-user-pool-client-default-redirect-uri/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-user-pool-client-default-redirect-uri/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-user-pool-client-default-redirect-uri" + }, + "integuserpoolclientdefaultredirecturitestDefaultTestDeployAssertF79A1D23.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integuserpoolclientdefaultredirecturitestDefaultTestDeployAssertF79A1D23.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integuserpoolclientdefaultredirecturitestDefaultTestDeployAssertF79A1D23": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integuserpoolclientdefaultredirecturitestDefaultTestDeployAssertF79A1D23.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integuserpoolclientdefaultredirecturitestDefaultTestDeployAssertF79A1D23.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "integuserpoolclientdefaultredirecturitestDefaultTestDeployAssertF79A1D23.assets" + ], + "metadata": { + "/integ-user-pool-client-default-redirect-uri-test/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-user-pool-client-default-redirect-uri-test/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integ-user-pool-client-default-redirect-uri-test/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/tree.json new file mode 100644 index 0000000000000..14caa9fd0d7d7 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.js.snapshot/tree.json @@ -0,0 +1,195 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "integ-user-pool-client-default-redirect-uri": { + "id": "integ-user-pool-client-default-redirect-uri", + "path": "integ-user-pool-client-default-redirect-uri", + "children": { + "myuserpool": { + "id": "myuserpool", + "path": "integ-user-pool-client-default-redirect-uri/myuserpool", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-user-pool-client-default-redirect-uri/myuserpool/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Cognito::UserPool", + "aws:cdk:cloudformation:props": { + "accountRecoverySetting": { + "recoveryMechanisms": [ + { + "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", + "smsVerificationMessage": "The verification code to your new account is {####}", + "verificationMessageTemplate": { + "defaultEmailOption": "CONFIRM_WITH_CODE", + "emailMessage": "The verification code to your new account is {####}", + "emailSubject": "Verify your new account", + "smsMessage": "The verification code to your new account is {####}" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cognito.CfnUserPool", + "version": "0.0.0" + } + }, + "myuserpoolclient": { + "id": "myuserpoolclient", + "path": "integ-user-pool-client-default-redirect-uri/myuserpool/myuserpoolclient", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-user-pool-client-default-redirect-uri/myuserpool/myuserpoolclient/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Cognito::UserPoolClient", + "aws:cdk:cloudformation:props": { + "allowedOAuthFlows": [ + "implicit", + "code" + ], + "allowedOAuthFlowsUserPoolClient": true, + "allowedOAuthScopes": [ + "profile", + "phone", + "email", + "openid", + "aws.cognito.signin.user.admin" + ], + "callbackUrLs": [ + "https://aaa.example.com", + "https://bbb.example.com", + "https://ccc.example.com" + ], + "defaultRedirectUri": "https://aaa.example.com", + "supportedIdentityProviders": [ + "COGNITO" + ], + "userPoolId": { + "Ref": "myuserpool01998219" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cognito.CfnUserPoolClient", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cognito.UserPoolClient", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cognito.UserPool", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integ-user-pool-client-default-redirect-uri/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integ-user-pool-client-default-redirect-uri/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "integ-user-pool-client-default-redirect-uri-test": { + "id": "integ-user-pool-client-default-redirect-uri-test", + "path": "integ-user-pool-client-default-redirect-uri-test", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "integ-user-pool-client-default-redirect-uri-test/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "integ-user-pool-client-default-redirect-uri-test/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "integ-user-pool-client-default-redirect-uri-test/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integ-user-pool-client-default-redirect-uri-test/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integ-user-pool-client-default-redirect-uri-test/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.ts new file mode 100644 index 0000000000000..0c5f5f0df7680 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cognito/test/integ.user-pool-client-default-redirect-uri.ts @@ -0,0 +1,21 @@ +import { App, RemovalPolicy, Stack } from 'aws-cdk-lib'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import { UserPool } from 'aws-cdk-lib/aws-cognito'; + +const app = new App(); +const stack = new Stack(app, 'integ-user-pool-client-default-redirect-uri'); + +const userpool = new UserPool(stack, 'myuserpool', { + removalPolicy: RemovalPolicy.DESTROY, +}); + +userpool.addClient('myuserpoolclient', { + oAuth: { + defaultRedirectUri: 'https://aaa.example.com', + callbackUrls: ['https://aaa.example.com', 'https://bbb.example.com', 'https://ccc.example.com'], + }, +}); + +new IntegTest(app, 'integ-user-pool-client-default-redirect-uri-test', { + testCases: [stack], +}); diff --git a/packages/aws-cdk-lib/aws-cognito/README.md b/packages/aws-cdk-lib/aws-cognito/README.md index 6e8928279f182..a5f9bf77c87bc 100644 --- a/packages/aws-cdk-lib/aws-cognito/README.md +++ b/packages/aws-cdk-lib/aws-cognito/README.md @@ -717,6 +717,24 @@ pool.addClient('app-client', { }); ``` +To set a default redirect URI, use the `defaultRedirectUri` property. +Its value must be present in the `callbackUrls` list. + +```ts +const pool = new cognito.UserPool(this, 'Pool'); +pool.addClient('app-client', { + oAuth: { + flows: { + authorizationCodeGrant: true, + }, + scopes: [ cognito.OAuthScope.OPENID ], + defaultRedirectUri: 'https://my-app-domain.com/welcome', + callbackUrls: [ 'https://my-app-domain.com/welcome', 'https://my-app-domain.com/hello' ], + logoutUrls: [ 'https://my-app-domain.com/signin' ], + }, +}); +``` + An app client can be configured to prevent user existence errors. This instructs the Cognito authentication API to return generic authentication failure responses instead of an UserNotFoundException. By default, the flag diff --git a/packages/aws-cdk-lib/aws-cognito/lib/user-pool-client.ts b/packages/aws-cdk-lib/aws-cognito/lib/user-pool-client.ts index 92bc7c4911811..d1e7ad218c1f5 100644 --- a/packages/aws-cdk-lib/aws-cognito/lib/user-pool-client.ts +++ b/packages/aws-cdk-lib/aws-cognito/lib/user-pool-client.ts @@ -3,7 +3,7 @@ import { CfnUserPoolClient } from './cognito.generated'; import { IUserPool } from './user-pool'; import { ClientAttributes } from './user-pool-attr'; import { IUserPoolResourceServer, ResourceServerScope } from './user-pool-resource-server'; -import { IResource, Resource, Duration, Stack, SecretValue } from '../../core'; +import { IResource, Resource, Duration, Stack, SecretValue, Token } from '../../core'; import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId } from '../../custom-resources'; /** @@ -66,6 +66,25 @@ export interface OAuthSettings { * @default [OAuthScope.PHONE,OAuthScope.EMAIL,OAuthScope.OPENID,OAuthScope.PROFILE,OAuthScope.COGNITO_ADMIN] */ readonly scopes?: OAuthScope[]; + + /** + * The default redirect URI. + * Must be in the `callbackUrls` list. + * + * A redirect URI must: + * * Be an absolute URI + * * Be registered with the authorization server. + * * Not include a fragment component. + * + * @see https://tools.ietf.org/html/rfc6749#section-3.1.2 + * + * Amazon Cognito requires HTTPS over HTTP except for http://localhost for testing purposes only. + * + * App callback URLs such as myapp://example are also supported. + * + * @default - no default redirect URI + */ + readonly defaultRedirectUri?: string; } /** @@ -407,6 +426,17 @@ export class UserPoolClient extends Resource implements IUserPoolClient { } } + if (props.oAuth?.defaultRedirectUri && !Token.isUnresolved(props.oAuth.defaultRedirectUri)) { + if (callbackUrls && !callbackUrls.includes(props.oAuth.defaultRedirectUri)) { + throw new Error('defaultRedirectUri must be included in callbackUrls.'); + } + + const defaultRedirectUriPattern = /^(?=.{1,1024}$)[\p{L}\p{M}\p{S}\p{N}\p{P}]+$/u; + if (!defaultRedirectUriPattern.test(props.oAuth.defaultRedirectUri)) { + throw new Error(`defaultRedirectUri must match the \`^(?=.{1,1024}$)[\p{L}\p{M}\p{S}\p{N}\p{P}]+$\` pattern, got ${props.oAuth.defaultRedirectUri}`); + } + } + if (!props.generateSecret && props.enablePropagateAdditionalUserContextData) { throw new Error('Cannot activate enablePropagateAdditionalUserContextData in an app client without a client secret.'); } @@ -421,6 +451,7 @@ export class UserPoolClient extends Resource implements IUserPoolClient { explicitAuthFlows: this.configureAuthFlows(props), allowedOAuthFlows: props.disableOAuth ? undefined : this.configureOAuthFlows(), allowedOAuthScopes: props.disableOAuth ? undefined : this.configureOAuthScopes(props.oAuth), + defaultRedirectUri: props.oAuth?.defaultRedirectUri, callbackUrLs: callbackUrls && callbackUrls.length > 0 && !props.disableOAuth ? callbackUrls : undefined, logoutUrLs: props.oAuth?.logoutUrls, allowedOAuthFlowsUserPoolClient: !props.disableOAuth, diff --git a/packages/aws-cdk-lib/aws-cognito/test/user-pool-client.test.ts b/packages/aws-cdk-lib/aws-cognito/test/user-pool-client.test.ts index 04941fa3204d3..751b722b6c2c7 100644 --- a/packages/aws-cdk-lib/aws-cognito/test/user-pool-client.test.ts +++ b/packages/aws-cdk-lib/aws-cognito/test/user-pool-client.test.ts @@ -1286,4 +1286,57 @@ describe('User Pool Client', () => { }), ).toThrow('Cannot activate enablePropagateAdditionalUserContextData in an app client without a client secret.'); }); + + test('defaulrRedirectUri in UserPoolClient', () => { + const stack = new Stack(); + const pool = new UserPool(stack, 'Pool'); + + // WHEN + new UserPoolClient(stack, 'PoolClient', { + userPool: pool, + oAuth: { + defaultRedirectUri: 'https://aaa.example.com', + callbackUrls: ['https://aaa.example.com', 'https://bbb.example.com', 'https://ccc.example.com'], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Cognito::UserPoolClient', { + DefaultRedirectURI: 'https://aaa.example.com', + CallbackURLs: ['https://aaa.example.com', 'https://bbb.example.com', 'https://ccc.example.com'], + }); + }); + + test('cannot create when defaultRedirectUri is not inclueded in callbackUrls', () => { + // GIVEN + const stack = new Stack(); + const pool = new UserPool(stack, 'Pool'); + + // WHEN + expect(() => new UserPoolClient(stack, 'PoolClient', { + userPool: pool, + oAuth: { + defaultRedirectUri: 'https://ddd.example.com', + callbackUrls: ['https://aaa.example.com', 'https://bbb.example.com', 'https://ccc.example.com'], + }, + }), + ).toThrow('defaultRedirectUri must be included in callbackUrls.'); + }); + + test('cannot create when invalid defaultRedirectUri is set', () => { + // GIVEN + const stack = new Stack(); + const pool = new UserPool(stack, 'Pool'); + + const invalidUrl = 'https://' + 'a'.repeat(1025) + '.example.com'; + // WHEN + expect(() => new UserPoolClient(stack, 'PoolClient', { + userPool: pool, + oAuth: { + defaultRedirectUri: invalidUrl, + callbackUrls: [invalidUrl, 'https://bbb.example.com', 'https://ccc.example.com'], + }, + }), + ).toThrow(`defaultRedirectUri must match the \`^(?=.{1,1024}$)[\p{L}\p{M}\p{S}\p{N}\p{P}]+$\` pattern, got ${invalidUrl}`); + }); + });