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(aws-cognito): add AuthSessionValidity property on a UserPoolClient #23040

Merged
merged 10 commits into from Dec 20, 2022
11 changes: 11 additions & 0 deletions packages/@aws-cdk/aws-cognito/README.md
Expand Up @@ -708,6 +708,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