Skip to content

Commit

Permalink
feat(aws-cognito): add AuthSessionValidity property on a UserPoolClie…
Browse files Browse the repository at this point in the history
…nt (#23040)

Fixes #22854

----

### All Submissions:

* [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md)

### Adding new Unconventional Dependencies:

* [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies)

### New Features

* [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)?
	* [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
drumnistnakano committed Dec 20, 2022
1 parent 606837d commit 8896fb9
Show file tree
Hide file tree
Showing 12 changed files with 422 additions and 288 deletions.
11 changes: 11 additions & 0 deletions packages/@aws-cdk/aws-cognito/README.md
Expand Up @@ -720,6 +720,17 @@ const client = pool.addClient('app-client', {
client.node.addDependency(provider);
```

The property `authSessionValidity` is the session token for each API request in the authentication flow.
Valid duration is from 3 to 15 minutes.

```ts
const pool = new cognito.UserPool(this, 'Pool');
pool.addClient('app-client', {
// ...
authSessionValidity: Duration.minutes(15),
});
```

In accordance with the OIDC open standard, Cognito user pool clients provide access tokens, ID tokens and refresh tokens.
More information is available at [Using Tokens with User Pools](https://docs.aws.amazon.com/en_us/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html).
The expiration time for these tokens can be configured as shown below.
Expand Down
15 changes: 15 additions & 0 deletions packages/@aws-cdk/aws-cognito/lib/user-pool-client.ts
Expand Up @@ -239,6 +239,15 @@ export interface UserPoolClientOptions {
*/
readonly oAuth?: OAuthSettings;

/**
* Cognito creates a session token for each API request in an authentication flow.
* AuthSessionValidity is the duration, in minutes, of that session token.
* see defaults in `AuthSessionValidity`. Valid duration is from 3 to 15 minutes.
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cognito-userpoolclient.html#cfn-cognito-userpoolclient-authsessionvalidity
* @default - Duration.minutes(3)
*/
readonly authSessionValidity?: Duration;

/**
* Whether Cognito returns a UserNotFoundException exception when the
* user does not exist in the user pool (false), or whether it returns
Expand Down Expand Up @@ -409,6 +418,7 @@ export class UserPoolClient extends Resource implements IUserPoolClient {
writeAttributes: props.writeAttributes?.attributes(),
enableTokenRevocation: props.enableTokenRevocation,
});
this.configureAuthSessionValidity(resource, props);
this.configureTokenValidity(resource, props);

this.userPoolClientId = resource.ref;
Expand Down Expand Up @@ -522,6 +532,11 @@ export class UserPoolClient extends Resource implements IUserPoolClient {
return Array.from(providers);
}

private configureAuthSessionValidity(resource: CfnUserPoolClient, props: UserPoolClientProps) {
this.validateDuration('authSessionValidity', Duration.minutes(3), Duration.minutes(15), props.authSessionValidity);
resource.authSessionValidity = props.authSessionValidity ? props.authSessionValidity.toMinutes() : undefined;
}

private configureTokenValidity(resource: CfnUserPoolClient, props: UserPoolClientProps) {
this.validateDuration('idTokenValidity', Duration.minutes(5), Duration.days(1), props.idTokenValidity);
this.validateDuration('accessTokenValidity', Duration.minutes(5), Duration.days(1), props.accessTokenValidity);
Expand Down

This file was deleted.

Large diffs are not rendered by default.

@@ -1 +1 @@
{"version":"20.0.0"}
{"version":"21.0.0"}
@@ -1,28 +1,28 @@
{
"version": "20.0.0",
"version": "21.0.0",
"files": {
"105b4f39ae68785e705640aa91919e412fcba2dd454aca53412747be8d955286": {
"a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476": {
"source": {
"path": "asset.105b4f39ae68785e705640aa91919e412fcba2dd454aca53412747be8d955286",
"path": "asset.a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476",
"packaging": "zip"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "105b4f39ae68785e705640aa91919e412fcba2dd454aca53412747be8d955286.zip",
"objectKey": "a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476.zip",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
},
"800ae0e8612e8f6d8cb727c55cf20dc51d4cbcc37c1e1701560675517ff5134a": {
"c2d925005ce1ea0db47e73cb0e76cc9f0f9347ede3ba8abe8f0768effe102872": {
"source": {
"path": "integ-user-pool-client-explicit-props.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "800ae0e8612e8f6d8cb727c55cf20dc51d4cbcc37c1e1701560675517ff5134a.json",
"objectKey": "c2d925005ce1ea0db47e73cb0e76cc9f0f9347ede3ba8abe8f0768effe102872.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
Expand Down
Expand Up @@ -59,6 +59,7 @@
"profile",
"aws.cognito.signin.user.admin"
],
"AuthSessionValidity": 3,
"CallbackURLs": [
"https://redirect-here.myapp.com"
],
Expand Down Expand Up @@ -203,7 +204,7 @@
"S3Bucket": {
"Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
},
"S3Key": "105b4f39ae68785e705640aa91919e412fcba2dd454aca53412747be8d955286.zip"
"S3Key": "a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476.zip"
},
"Role": {
"Fn::GetAtt": [
Expand Down
@@ -1,5 +1,5 @@
{
"version": "20.0.0",
"version": "21.0.0",
"testCases": {
"integ.user-pool-client-explicit-props": {
"stacks": [
Expand Down
@@ -1,12 +1,6 @@
{
"version": "20.0.0",
"version": "21.0.0",
"artifacts": {
"Tree": {
"type": "cdk:tree",
"properties": {
"file": "tree.json"
}
},
"integ-user-pool-client-explicit-props.assets": {
"type": "cdk:asset-manifest",
"properties": {
Expand All @@ -23,7 +17,7 @@
"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}/800ae0e8612e8f6d8cb727c55cf20dc51d4cbcc37c1e1701560675517ff5134a.json",
"stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/c2d925005ce1ea0db47e73cb0e76cc9f0f9347ede3ba8abe8f0768effe102872.json",
"requiresBootstrapStackVersion": 6,
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version",
"additionalDependencies": [
Expand Down Expand Up @@ -95,6 +89,12 @@
]
},
"displayName": "integ-user-pool-client-explicit-props"
},
"Tree": {
"type": "cdk:tree",
"properties": {
"file": "tree.json"
}
}
}
}
Expand Up @@ -4,14 +4,6 @@
"id": "App",
"path": "",
"children": {
"Tree": {
"id": "Tree",
"path": "Tree",
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.85"
}
},
"integ-user-pool-client-explicit-props": {
"id": "integ-user-pool-client-explicit-props",
"path": "integ-user-pool-client-explicit-props",
Expand Down Expand Up @@ -92,6 +84,7 @@
"profile",
"aws.cognito.signin.user.admin"
],
"authSessionValidity": 3,
"callbackUrLs": [
"https://redirect-here.myapp.com"
],
Expand Down Expand Up @@ -156,14 +149,14 @@
"id": "Default",
"path": "integ-user-pool-client-explicit-props/myuserpool/myuserpoolclient/DescribeCognitoUserPoolClient/Resource/Default",
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.85"
"fqn": "@aws-cdk/core.CfnResource",
"version": "0.0.0"
}
}
},
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.85"
"fqn": "@aws-cdk/core.CustomResource",
"version": "0.0.0"
}
},
"CustomResourcePolicy": {
Expand Down Expand Up @@ -236,6 +229,14 @@
"id": "ServiceRole",
"path": "integ-user-pool-client-explicit-props/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole",
"children": {
"ImportServiceRole": {
"id": "ImportServiceRole",
"path": "integ-user-pool-client-explicit-props/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole/ImportServiceRole",
"constructInfo": {
"fqn": "@aws-cdk/core.Resource",
"version": "0.0.0"
}
},
"Resource": {
"id": "Resource",
"path": "integ-user-pool-client-explicit-props/AWS679f53fac002430cb0da5b7982bd2287/ServiceRole/Resource",
Expand Down Expand Up @@ -289,8 +290,8 @@
"id": "Stage",
"path": "integ-user-pool-client-explicit-props/AWS679f53fac002430cb0da5b7982bd2287/Code/Stage",
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.85"
"fqn": "@aws-cdk/core.AssetStaging",
"version": "0.0.0"
}
},
"AssetBucket": {
Expand All @@ -317,7 +318,7 @@
"s3Bucket": {
"Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
},
"s3Key": "105b4f39ae68785e705640aa91919e412fcba2dd454aca53412747be8d955286.zip"
"s3Key": "a268caa53756f51bda8ad5f499be4ed8484a81b314811806fbb66f874837c476.zip"
},
"role": {
"Fn::GetAtt": [
Expand Down Expand Up @@ -369,17 +370,41 @@
"fqn": "@aws-cdk/aws-secretsmanager.Secret",
"version": "0.0.0"
}
},
"BootstrapVersion": {
"id": "BootstrapVersion",
"path": "integ-user-pool-client-explicit-props/BootstrapVersion",
"constructInfo": {
"fqn": "@aws-cdk/core.CfnParameter",
"version": "0.0.0"
}
},
"CheckBootstrapVersion": {
"id": "CheckBootstrapVersion",
"path": "integ-user-pool-client-explicit-props/CheckBootstrapVersion",
"constructInfo": {
"fqn": "@aws-cdk/core.CfnRule",
"version": "0.0.0"
}
}
},
"constructInfo": {
"fqn": "@aws-cdk/core.Stack",
"version": "0.0.0"
}
},
"Tree": {
"id": "Tree",
"path": "Tree",
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.85"
"version": "10.1.161"
}
}
},
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.85"
"fqn": "@aws-cdk/core.App",
"version": "0.0.0"
}
}
}
@@ -1,5 +1,5 @@
import { Secret } from '@aws-cdk/aws-secretsmanager';
import { App, RemovalPolicy, Stack } from '@aws-cdk/core';
import { App, Duration, RemovalPolicy, Stack } from '@aws-cdk/core';
import { ClientAttributes, OAuthScope, StringAttribute, UserPool } from '../lib';

const app = new App();
Expand Down Expand Up @@ -37,6 +37,7 @@ const client = userpool.addClient('myuserpoolclient', {
callbackUrls: ['https://redirect-here.myapp.com'],
},
preventUserExistenceErrors: true,
authSessionValidity: Duration.minutes(3),
writeAttributes: (new ClientAttributes()).withStandardAttributes(
{
address: true,
Expand Down
80 changes: 80 additions & 0 deletions packages/@aws-cdk/aws-cognito/test/user-pool-client.test.ts
Expand Up @@ -777,6 +777,86 @@ describe('User Pool Client', () => {
});
});

describe('auth session validity', () => {
test('default', () => {
// GIVEN
const stack = new Stack();
const pool = new UserPool(stack, 'Pool');

// WHEN
pool.addClient('Client1', {
userPoolClientName: 'Client1',
authSessionValidity: Duration.minutes(3),
});
pool.addClient('Client2', {
userPoolClientName: 'Client2',
authSessionValidity: Duration.minutes(9),
});
pool.addClient('Client3', {
userPoolClientName: 'Client3',
authSessionValidity: Duration.minutes(15),
});
pool.addClient('Client5', {
userPoolClientName: 'Client4',
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::Cognito::UserPoolClient', {
ClientName: 'Client1',
AuthSessionValidity: 3,
});
Template.fromStack(stack).hasResourceProperties('AWS::Cognito::UserPoolClient', {
ClientName: 'Client2',
AuthSessionValidity: 9,
});
Template.fromStack(stack).hasResourceProperties('AWS::Cognito::UserPoolClient', {
ClientName: 'Client3',
AuthSessionValidity: 15,
});
Template.fromStack(stack).hasResourceProperties('AWS::Cognito::UserPoolClient', {
ClientName: 'Client4',
});
});

test.each([
Duration.minutes(0),
Duration.minutes(1),
Duration.minutes(3).minus(Duration.minutes(1)),
Duration.minutes(15).plus(Duration.minutes(1)),
Duration.minutes(100),
])('validates authSessionValidity is a duration between 3 and 15 minutes', (validity) => {
const stack = new Stack();
const pool = new UserPool(stack, 'Pool');
expect(() => {
pool.addClient('Client1', {
userPoolClientName: 'Client1',
authSessionValidity: validity,
});
}).toThrow(`authSessionValidity: Must be a duration between 3 minutes and 15 minutes (inclusive); received ${validity.toHumanString()}.`);
});

test.each([
Duration.minutes(3),
Duration.minutes(9),
Duration.minutes(15),
])('validates authSessionValidity is a duration between 3 and 15 minutes (valid)', (validity) => {
const stack = new Stack();
const pool = new UserPool(stack, 'Pool');

// WHEN
pool.addClient('Client1', {
userPoolClientName: 'Client1',
authSessionValidity: validity,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::Cognito::UserPoolClient', {
ClientName: 'Client1',
AuthSessionValidity: validity.toMinutes(),
});
});
});

describe('token validity', () => {
test('default', () => {
// GIVEN
Expand Down

0 comments on commit 8896fb9

Please sign in to comment.