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 identity provider with support for Facebook & Amazon #8134

Merged
merged 14 commits into from
Jun 3, 2020
33 changes: 32 additions & 1 deletion 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)
- [Identity Providers](#identity-providers)
- [Domains](#domains)

## User Pools
Expand Down Expand Up @@ -414,6 +415,36 @@ pool.addClient('app-client', {
});
```

### Identity Providers

Users that are part of a user pool can sign in either directly through a user pool, or federate through a third-party
identity provider. Once configured, the Cognito backend will take care of integrating with the third-party provider.
Read more about [Adding User Pool Sign-in Through a Third
Party](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-identity-federation.html).

The following third-party identity providers are currentlhy supported in the CDK -

* [Login With Amazon](https://developer.amazon.com/apps-and-games/login-with-amazon)
* [Facebook Login](https://developers.facebook.com/docs/facebook-login/)

The following code configures a user pool to federate with the third party provider, 'Login with Amazon'. The identity
provider needs to be configured with a set of credentials that the Cognito backend can use to federate with the
third-party identity provider.

```ts
const userpool = new UserPool(stack, 'Pool');

const provider = UserPoolIdentityProvider.amazon(stack, 'Amazon', {
clientId: 'amzn-client-id',
clientSecret: 'amzn-client-secret',
userPool: userpool,
});
```

In order to allow users to sign in with a third-party identity provider, the app client that faces the user should be
configured to use the identity provider. See [App Clients](#app-clients) section to know more about App Clients.
The identity providers should be configured on `identityProviders` property available on the `UserPoolClient` construct.

### Domains

After setting up an [app client](#app-clients), the address for the user pool's sign-up and sign-in webpages can be
Expand Down Expand Up @@ -443,4 +474,4 @@ pool.addDomain('CustomDomain', {

Read more about [Using the Amazon Cognito
Domain](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-assign-domain-prefix.html) and [Using Your Own
Domain](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-add-custom-domain.html).
Domain](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-add-custom-domain.html).
4 changes: 3 additions & 1 deletion packages/@aws-cdk/aws-cognito/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ export * from './cognito.generated';
export * from './user-pool';
export * from './user-pool-attr';
export * from './user-pool-client';
export * from './user-pool-domain';
export * from './user-pool-domain';
export * from './user-pool-idp';
export * from './user-pool-idps';
28 changes: 28 additions & 0 deletions packages/@aws-cdk/aws-cognito/lib/user-pool-client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Construct, IResource, Resource } from '@aws-cdk/core';
import { CfnUserPoolClient } from './cognito.generated';
import { IUserPool } from './user-pool';
import { IUserPoolIdentityProvider } from './user-pool-idp';

/**
* Types of authentication flow
Expand Down Expand Up @@ -182,6 +183,20 @@ export interface UserPoolClientOptions {
* @default true for new stacks
*/
readonly preventUserExistenceErrors?: boolean;

/**
* Whether users registered in the user pool should be able to sign in using this client.
* If this is set to `true` and `identityProviders` are set, the client will only allow sign in via
* third-party identity providers.
* @default true
*/
readonly allowUserPoolIdentities?: boolean;

/**
* The list of federated identity providers that users should be able to use to sign in using this client.
* @default - no identity providers
*/
readonly identityProviders?: IUserPoolIdentityProvider[];
}

/**
Expand Down Expand Up @@ -244,6 +259,7 @@ export class UserPoolClient extends Resource implements IUserPoolClient {
callbackUrLs: (props.oAuth?.callbackUrls && props.oAuth?.callbackUrls.length > 0) ? props.oAuth?.callbackUrls : undefined,
allowedOAuthFlowsUserPoolClient: props.oAuth ? true : undefined,
preventUserExistenceErrors: this.configurePreventUserExistenceErrors(props.preventUserExistenceErrors),
supportedIdentityProviders: this.configureIdentityProviders(props),
});

this.userPoolClientId = resource.ref;
Expand Down Expand Up @@ -314,4 +330,16 @@ export class UserPoolClient extends Resource implements IUserPoolClient {
}
return prevent ? 'ENABLED' : 'LEGACY';
}

private configureIdentityProviders(props: UserPoolClientProps): string[] | undefined {
const providers: Set<string> = new Set();
if (props.allowUserPoolIdentities === undefined || props.allowUserPoolIdentities === true) {
providers.add('COGNITO');
}
if (props.identityProviders) {
props.identityProviders.forEach((p) => providers.add(p.providerName));
}
if (providers.size === 0) { return undefined; }
return Array.from(providers);
}
}
41 changes: 41 additions & 0 deletions packages/@aws-cdk/aws-cognito/lib/user-pool-idp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Construct, IResource } from '@aws-cdk/core';
import {
UserPoolIdentityProviderAmazon,
UserPoolIdentityProviderAmazonProps,
UserPoolIdentityProviderFacebook,
UserPoolIdentityProviderFacebookProps,
} from './user-pool-idps';

/**
* Represents a UserPoolIdentityProvider
*/
export interface IUserPoolIdentityProvider extends IResource {
/**
* The primary identifier of this identity provider
* @attribute
*/
readonly providerName: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be typed UserPoolClientIdentityProvider.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not so confident that they are one and the same. They are the same at the moment, for the providers I've chosen to implement.

Ex: the list here does not match up with here.

It's not clear yet if this is a documentation gap, or something more.

Leaving this as is for now.

}

/**
* Options to integrate with the various social identity providers.
*/
export class UserPoolIdentityProvider {
/**
* Federate with 'Facebook Login'
* @see https://developers.facebook.com/docs/facebook-login/
*/
public static facebook(scope: Construct, id: string, options: UserPoolIdentityProviderFacebookProps) {
return new UserPoolIdentityProviderFacebook(scope, id, options);
}

/**
* Federate with 'Login with Amazon'
* @see https://developer.amazon.com/apps-and-games/login-with-amazon
*/
public static amazon(scope: Construct, id: string, options: UserPoolIdentityProviderAmazonProps) {
return new UserPoolIdentityProviderAmazon(scope, id, options);
}

private constructor() {}
}
58 changes: 58 additions & 0 deletions packages/@aws-cdk/aws-cognito/lib/user-pool-idps/amazon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Construct, Resource } from '@aws-cdk/core';
import { CfnUserPoolIdentityProvider } from '../cognito.generated';
import { IUserPool } from '../user-pool';
import { IUserPoolIdentityProvider } from '../user-pool-idp';

/**
* Properties to initialize UserPoolAmazonIdentityProvider
*/
export interface UserPoolIdentityProviderAmazonProps {
/**
* The user pool to which this construct provides identities.
*/
readonly userPool: IUserPool;

/**
* The client id recognized by 'Login with Amazon' APIs.
* @see https://developer.amazon.com/docs/login-with-amazon/security-profile.html#client-identifier
*/
readonly clientId: string;
/**
* The client secret to be accompanied with clientId for 'Login with Amazon' APIs to authenticate the client.
* @see https://developer.amazon.com/docs/login-with-amazon/security-profile.html#client-identifier
*/
readonly clientSecret: string;
/**
* The types of user profile data to obtain for the Amazon profile.
* @see https://developer.amazon.com/docs/login-with-amazon/customer-profile.html
* @default [ profile ]
*/
readonly scopes?: string[];
}

/**
* Represents a identity provider that integrates with 'Login with Amazon'
* @resource AWS::Cognito::UserPoolIdentityProvider
*/
export class UserPoolIdentityProviderAmazon extends Resource implements IUserPoolIdentityProvider {
public readonly providerName: string;

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

const scopes = props.scopes ?? [ 'profile' ];

const resource = new CfnUserPoolIdentityProvider(this, 'Resource', {
userPoolId: props.userPool.userPoolId,
providerName: 'LoginWithAmazon', // must be 'LoginWithAmazon' when the type is 'LoginWithAmazon'
providerType: 'LoginWithAmazon',
providerDetails: {
client_id: props.clientId,
client_secret: props.clientSecret,
authorize_scopes: scopes.join(' '),
},
});

this.providerName = super.getResourceNameAttribute(resource.ref);
}
}
63 changes: 63 additions & 0 deletions packages/@aws-cdk/aws-cognito/lib/user-pool-idps/facebook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Construct, Resource } from '@aws-cdk/core';
import { CfnUserPoolIdentityProvider } from '../cognito.generated';
import { IUserPool } from '../user-pool';
import { IUserPoolIdentityProvider } from '../user-pool-idp';

/**
* Properties to initialize UserPoolFacebookIdentityProvider
*/
export interface UserPoolIdentityProviderFacebookProps {
/**
* The user pool to which this construct provides identities.
*/
readonly userPool: IUserPool;

/**
* The client id recognized by Facebook APIs.
*/
readonly clientId: string;
/**
* The client secret to be accompanied with clientUd for Facebook to authenticate the client.
* @see https://developers.facebook.com/docs/facebook-login/security#appsecret
*/
readonly clientSecret: string;
/**
* The list of facebook permissions to obtain for getting access to the Facebook profile.
* @see https://developers.facebook.com/docs/facebook-login/permissions
* @default [ public_profile ]
*/
readonly scopes?: string[];
/**
* The Facebook API version to use
* @default - to the oldest version supported by Facebook
*/
readonly apiVersion?: string;
}

/**
* Represents a identity provider that integrates with 'Facebook Login'
* @resource AWS::Cognito::UserPoolIdentityProvider
*/
export class UserPoolIdentityProviderFacebook extends Resource implements IUserPoolIdentityProvider {
public readonly providerName: string;

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

const scopes = props.scopes ?? [ 'public_profile' ];

const resource = new CfnUserPoolIdentityProvider(this, 'Resource', {
userPoolId: props.userPool.userPoolId,
providerName: 'Facebook', // must be 'Facebook' when the type is 'Facebook'
providerType: 'Facebook',
providerDetails: {
client_id: props.clientId,
client_secret: props.clientSecret,
authorize_scopes: scopes.join(','),
api_version: props.apiVersion,
},
});

this.providerName = super.getResourceNameAttribute(resource.ref);
}
}
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-cognito/lib/user-pool-idps/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './amazon';
export * from './facebook';
4 changes: 3 additions & 1 deletion packages/@aws-cdk/aws-cognito/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@
"exclude": [
"attribute-tag:@aws-cdk/aws-cognito.UserPoolClient.userPoolClientName",
"resource-attribute:@aws-cdk/aws-cognito.UserPoolClient.userPoolClientClientSecret",
"props-physical-name:@aws-cdk/aws-cognito.UserPoolDomainProps"
"props-physical-name:@aws-cdk/aws-cognito.UserPoolDomainProps",
"props-physical-name:@aws-cdk/aws-cognito.UserPoolIdentityProviderFacebookProps",
"props-physical-name:@aws-cdk/aws-cognito.UserPoolIdentityProviderAmazonProps"
]
},
"stability": "experimental",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,11 @@
"ALLOW_REFRESH_TOKEN_AUTH"
],
"GenerateSecret": true,
"PreventUserExistenceErrors": "ENABLED"
"PreventUserExistenceErrors": "ENABLED",
"SupportedIdentityProviders": [
"COGNITO"
]
}
}
}
}
}
Loading