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): user pool domain #7224

Merged
merged 14 commits into from
Apr 22, 2020
32 changes: 32 additions & 0 deletions packages/@aws-cdk/aws-cognito/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aw
- [Lambda Triggers](#lambda-triggers)
- [Import](#importing-user-pools)
- [App Clients](#app-clients)
- [Domains](#domains)

## User Pools

Expand Down Expand Up @@ -398,3 +399,34 @@ pool.addClient('app-client', {
}
});
```

### Domains

After setting up an [app client](#app-clients), the address for the user pool's sign-up and sign-in webpages can be
configured using domains. There are two ways to set up a domain - either the Amazon Cognito hosted domain can be chosen
with an available domain prefix, or a custom domain name can be chosen. The custom domain must be one that is already
owned, and whose certificate is registered in AWS Certificate Manager.

The following code sets up a user pool domain in Amazon Cognito hosted domain with the prefix 'my-awesome-app' -

```ts
const pool = new UserPool(this, 'Pool');
pool.addDomain('domain', {
domain: UserPoolDomainType.cognitoDomain({
domainPrefix: 'my-awesome-app',
}),
});
```

On the other hand, the following code sets up a user pool domain and use your own custom domain -

```ts
const domainCert = new acm.Certificate.fromCertificateArn(this, 'domainCert', certificateArn);
const pool = new UserPool(this, 'Pool');
pool.addDomain('domain', {
domain: UserPoolDomainType.customDomain({
domainPrefix: 'my-awesome-app',
certificate: domainCert,
}),
});
```
3 changes: 2 additions & 1 deletion packages/@aws-cdk/aws-cognito/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export * from './cognito.generated';

export * from './user-pool';
export * from './user-pool-attr';
export * from './user-pool-client';
export * from './user-pool-client';
export * from './user-pool-domain';
2 changes: 1 addition & 1 deletion packages/@aws-cdk/aws-cognito/lib/user-pool-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export class OAuthScope {
}

/**
* Properties for the UserPoolClient construct
* Options to create a UserPoolClient
*/
export interface UserPoolClientOptions {
/**
Expand Down
129 changes: 129 additions & 0 deletions packages/@aws-cdk/aws-cognito/lib/user-pool-domain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { ICertificate } from '@aws-cdk/aws-certificatemanager';
import { Construct, IResource, Resource } from '@aws-cdk/core';
import { AwsCustomResource, AwsCustomResourcePolicy, AwsSdkCall, PhysicalResourceId } from '@aws-cdk/custom-resources';
import { CfnUserPoolDomain } from './cognito.generated';
import { IUserPool } from './user-pool';

/**
* Represents a user pool domain.
*/
export interface IUserPoolDomain extends IResource {
/**
* The domain that was specified to be created.
* If `customDomain` was selected, this holds the full domain name that was specified.
* If the `cognitoDomain` was used, it contains the prefix to the Cognito hosted domain.
* @attribute
*/
readonly domainName: string;
}

/**
* Options while specifying custom domain
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-add-custom-domain.html
*/
export interface CustomDomainOptions {
/**
* The custom domain name that you would like to associate with this User Pool.
*/
readonly domainName: string;

/**
* The certificate to associate with this domain.
*/
readonly certificate: ICertificate;
}

/**
* Options while specifying a cognito prefix domain.
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-assign-domain-prefix.html
*/
export interface CognitoDomainOptions {
/**
* The prefix to the Cognito hosted domain name that will be associated with the user pool.
*/
readonly domainPrefix: string;
}

/**
* Options to create a UserPoolDomain
*/
export interface UserPoolDomainOptions {
/**
* Associate a custom domain with your user pool
* Either `customDomain` or `cognitoDomain` must be specified.
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-add-custom-domain.html
* @default - not set if `cognitoDomain` is specified, otherwise, throws an error.
*/
readonly customDomain?: CustomDomainOptions;

/**
* Associate a cognito prefix domain with your user pool
* Either `customDomain` or `cognitoDomain` must be specified.
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-assign-domain-prefix.html
* @default - not set if `customDomain` is specified, otherwise, throws an error.
*/
readonly cognitoDomain?: CognitoDomainOptions;
}

/**
* Props for UserPoolDomain construct
*/
export interface UserPoolDomainProps extends UserPoolDomainOptions {
/**
* The user pool to which this domain should be associated.
*/
readonly userPool: IUserPool;
}

/**
* Define a user pool domain
*/
export class UserPoolDomain extends Resource implements IUserPoolDomain {
public readonly domainName: string;

constructor(scope: Construct, id: string, props: UserPoolDomainProps) {
super(scope, id);

if (!!props.customDomain === !!props.cognitoDomain) {
throw new Error('One of, and only one of, cognitoDomain or customDomain must be specified');
}

if (props.cognitoDomain?.domainPrefix && !/^[a-z0-9-]+$/.test(props.cognitoDomain.domainPrefix)) {
throw new Error('domainPrefix for cognitoDomain can contain only lowercase alphabets, numbers and hyphens');
}

const domainName = props.cognitoDomain?.domainPrefix || props.customDomain?.domainName!;
const resource = new CfnUserPoolDomain(this, 'Resource', {
userPoolId: props.userPool.userPoolId,
domain: domainName,
customDomainConfig: props.customDomain ? { certificateArn: props.customDomain.certificate.certificateArn } : undefined,
});

this.domainName = resource.ref;
}

/**
* The domain name of the CloudFront distribution associated with the user pool domain.
*/
public get cloudFrontDomainName(): string {
nija-at marked this conversation as resolved.
Show resolved Hide resolved
const sdkCall: AwsSdkCall = {
service: 'CognitoIdentityServiceProvider',
action: 'describeUserPoolDomain',
parameters: {
Domain: this.domainName,
},
physicalResourceId: PhysicalResourceId.of(this.domainName),
};
const customResource = new AwsCustomResource(this, 'CloudFrontDomainName', {
resourceType: 'Custom::UserPoolCloudFrontDomainName',
onCreate: sdkCall,
onUpdate: sdkCall,
policy: AwsCustomResourcePolicy.fromSdkCalls({
// DescribeUserPoolDomain only supports access level '*'
// https://docs.aws.amazon.com/IAM/latest/UserGuide/list_amazoncognitouserpools.html#amazoncognitouserpools-actions-as-permissions
resources: [ '*' ],
}),
});
return customResource.getResponseField('DomainDescription.CloudFrontDistribution');
}
}
16 changes: 16 additions & 0 deletions packages/@aws-cdk/aws-cognito/lib/user-pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Construct, Duration, IResource, Lazy, Resource, Stack } from '@aws-cdk/
import { CfnUserPool } from './cognito.generated';
import { ICustomAttribute, RequiredAttributes } from './user-pool-attr';
import { IUserPoolClient, UserPoolClient, UserPoolClientOptions } from './user-pool-client';
import { UserPoolDomain, UserPoolDomainOptions } from './user-pool-domain';

/**
* The different ways in which users of this pool can sign up or sign in.
Expand Down Expand Up @@ -658,13 +659,28 @@ export class UserPool extends Resource implements IUserPool {
(this.triggers as any)[operation.operationName] = fn.functionArn;
}

/**
* Add a new app client to this user pool.
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-client-apps.html
*/
public addClient(id: string, options?: UserPoolClientOptions): IUserPoolClient {
return new UserPoolClient(this, id, {
userPool: this,
...options,
});
}

/**
* Associate a domain to this user pool.
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-assign-domain.html
*/
public addDomain(id: string, options: UserPoolDomainOptions): UserPoolDomain {
return new UserPoolDomain(this, id, {
userPool: this,
...options,
});
}

private addLambdaPermission(fn: lambda.IFunction, name: string): void {
const capitalize = name.charAt(0).toUpperCase() + name.slice(1);
fn.addPermission(`${capitalize}Cognito`, {
Expand Down
7 changes: 6 additions & 1 deletion packages/@aws-cdk/aws-cognito/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,20 @@
"pkglint": "0.0.0"
},
"dependencies": {
"@aws-cdk/aws-certificatemanager": "0.0.0",
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-lambda": "0.0.0",
"@aws-cdk/core": "0.0.0",
"@aws-cdk/custom-resources": "0.0.0",
"constructs": "^3.0.2"
},
"homepage": "https://github.com/aws/aws-cdk",
"peerDependencies": {
"@aws-cdk/aws-certificatemanager": "0.0.0",
"@aws-cdk/aws-iam": "0.0.0",
"@aws-cdk/aws-lambda": "0.0.0",
"@aws-cdk/core": "0.0.0",
"@aws-cdk/custom-resources": "0.0.0",
"constructs": "^3.0.2"
},
"jest": {},
Expand All @@ -91,7 +95,8 @@
"awslint": {
"exclude": [
"attribute-tag:@aws-cdk/aws-cognito.UserPoolClient.userPoolClientName",
"resource-attribute:@aws-cdk/aws-cognito.UserPoolClient.userPoolClientClientSecret"
"resource-attribute:@aws-cdk/aws-cognito.UserPoolClient.userPoolClientClientSecret",
"props-physical-name:@aws-cdk/aws-cognito.UserPoolDomainProps"
]
},
"stability": "experimental",
Expand Down
Loading