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 pools - sign in with apple #13160

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-cognito/README.md
Expand Up @@ -418,6 +418,7 @@ The following third-party identity providers are currently 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/)
- [Google Login](https://developers.google.com/identity/sign-in/web/sign-in)
- [Sing In With Apple](https://developer.apple.com/sign-in-with-apple/get-started/)
piotrmoszkowicz marked this conversation as resolved.
Show resolved Hide resolved

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
Expand Down
6 changes: 6 additions & 0 deletions packages/@aws-cdk/aws-cognito/lib/user-pool-client.ts
Expand Up @@ -157,6 +157,12 @@ export class OAuthScope {
* Identity providers supported by the UserPoolClient
*/
export class UserPoolClientIdentityProvider {
/**
* Allow users to sign in using 'Sign In With Apple'.
* A `UserPoolIdentityProviderApple` must be attached to the user pool.
*/
public static readonly APPLE = new UserPoolClientIdentityProvider('SignInWithApple');

/**
* Allow users to sign in using 'Facebook Login'.
* A `UserPoolIdentityProviderFacebook` must be attached to the user pool.
Expand Down
63 changes: 63 additions & 0 deletions packages/@aws-cdk/aws-cognito/lib/user-pool-idps/apple.ts
@@ -0,0 +1,63 @@
import { Construct } from 'constructs';
import { CfnUserPoolIdentityProvider } from '../cognito.generated';
import { UserPoolIdentityProviderProps } from './base';
import { UserPoolIdentityProviderBase } from './private/user-pool-idp-base';

/**
* Properties to initialize UserPoolAppleIdentityProvider
*/
export interface UserPoolIdentityProviderAppleProps extends UserPoolIdentityProviderProps {
/**
* The client id recognized by Apple APIs.
* @see https://developer.apple.com/documentation/sign_in_with_apple/clientconfigi/3230948-clientid
*/
readonly clientId: string;
/**
* The teamId for Apple APIs to authenticate the client.
*/
readonly teamId: string;
/**
* The keyId (which has to be later supplied as `privateKey`) for Apple APIs to authenticate the client.
piotrmoszkowicz marked this conversation as resolved.
Show resolved Hide resolved
*/
readonly keyId: string;
/**
* The privateKey content for Apple APIs to authenticate the client.
*/
readonly privateKey: string;
/**
* The list of apple permissions to obtain for getting access to the apple profile
* @see https://developer.apple.com/documentation/sign_in_with_apple/clientconfigi/3230955-scope
* @default [ name ]
*/
readonly scopes?: string[];
}

/**
* Represents a identity provider that integrates with 'Apple'
* @resource AWS::Cognito::UserPoolIdentityProvider
*/
export class UserPoolIdentityProviderApple extends UserPoolIdentityProviderBase {
public readonly providerName: string;

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

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

const resource = new CfnUserPoolIdentityProvider(this, 'Resource', {
userPoolId: props.userPool.userPoolId,
providerName: 'SignInWithApple', // must be 'SignInWithApple' when the type is 'SignInWithApple'
providerType: 'SignInWithApple',
providerDetails: {
client_id: props.clientId,
team_id: props.teamId,
key_id: props.keyId,
private_key: props.privateKey,
authorize_scopes: scopes.join(' '),
},
attributeMapping: super.configureAttributeMapping(),
});

this.providerName = super.getResourceNameAttribute(resource.ref);
}
}
9 changes: 9 additions & 0 deletions packages/@aws-cdk/aws-cognito/lib/user-pool-idps/base.ts
Expand Up @@ -4,6 +4,15 @@ import { IUserPool } from '../user-pool';
* An attribute available from a third party identity provider.
*/
export class ProviderAttribute {
/** The email attribute provided by Apple */
public static readonly APPLE_EMAIL = new ProviderAttribute('email');
/** The name attribute provided by Apple */
public static readonly APPLE_NAME = new ProviderAttribute('name');
/** The first name attribute provided by Apple */
public static readonly APPLE_FIRST_NAME = new ProviderAttribute('firstName');
/** The last name attribute provided by Apple */
public static readonly APPLE_LAST_NAME = new ProviderAttribute('lastName');

/** The user id attribute provided by Amazon */
public static readonly AMAZON_USER_ID = new ProviderAttribute('user_id');
/** The email attribute provided by Amazon */
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-cognito/lib/user-pool-idps/index.ts
@@ -1,4 +1,5 @@
export * from './base';
export * from './apple';
export * from './amazon';
export * from './facebook';
export * from './google';
3 changes: 2 additions & 1 deletion packages/@aws-cdk/aws-cognito/package.json
Expand Up @@ -110,7 +110,8 @@
"props-physical-name:@aws-cdk/aws-cognito.UserPoolDomainProps",
"props-physical-name:@aws-cdk/aws-cognito.UserPoolIdentityProviderFacebookProps",
"props-physical-name:@aws-cdk/aws-cognito.UserPoolIdentityProviderAmazonProps",
"props-physical-name:@aws-cdk/aws-cognito.UserPoolIdentityProviderGoogleProps"
"props-physical-name:@aws-cdk/aws-cognito.UserPoolIdentityProviderGoogleProps",
"props-physical-name:@aws-cdk/aws-cognito.UserPoolIdentityProviderAppleProps"
]
},
"stability": "stable",
Expand Down
3 changes: 2 additions & 1 deletion packages/@aws-cdk/aws-cognito/test/user-pool-client.test.ts
Expand Up @@ -487,13 +487,14 @@ describe('User Pool Client', () => {
UserPoolClientIdentityProvider.FACEBOOK,
UserPoolClientIdentityProvider.AMAZON,
UserPoolClientIdentityProvider.GOOGLE,
UserPoolClientIdentityProvider.APPLE,
],
});

// THEN
expect(stack).toHaveResource('AWS::Cognito::UserPoolClient', {
ClientName: 'AllEnabled',
SupportedIdentityProviders: ['COGNITO', 'Facebook', 'LoginWithAmazon', 'Google'],
SupportedIdentityProviders: ['COGNITO', 'Facebook', 'LoginWithAmazon', 'Google', 'SignInWithApple'],
});
});

Expand Down
113 changes: 113 additions & 0 deletions packages/@aws-cdk/aws-cognito/test/user-pool-idps/apple.ts
@@ -0,0 +1,113 @@
import '@aws-cdk/assert/jest';
import { Stack } from '@aws-cdk/core';
import { ProviderAttribute, UserPool, UserPoolIdentityProviderApple } from '../../lib';

describe('UserPoolIdentityProvider', () => {
describe('apple', () => {
test('defaults', () => {
// GIVEN
const stack = new Stack();
const pool = new UserPool(stack, 'userpool');

// WHEN
new UserPoolIdentityProviderApple(stack, 'userpoolidp', {
userPool: pool,
clientId: 'com.amzn.cdk',
teamId: 'CDKTEAMCDK',
keyId: 'CDKKEYCDK1',
privateKey: 'PRIV_KEY_CDK',
});

expect(stack).toHaveResource('AWS::Cognito::UserPoolIdentityProvider', {
ProviderName: 'SignInWithApple',
ProviderType: 'SignInWithApple',
ProviderDetails: {
client_id: 'com.amzn.cdk',
team_id: 'CDKTEAMCDK',
key_id: 'CDKKEYCDK1',
private_key: 'PRIV_KEY_CDK',
authorize_scopes: 'name',
},
});
});

test('scopes', () => {
// GIVEN
const stack = new Stack();
const pool = new UserPool(stack, 'userpool');

// WHEN
new UserPoolIdentityProviderApple(stack, 'userpoolidp', {
userPool: pool,
clientId: 'com.amzn.cdk',
teamId: 'CDKTEAMCDK',
keyId: 'CDKKEYCDK1',
privateKey: 'PRIV_KEY_CDK',
scopes: ['scope1', 'scope2'],
});

expect(stack).toHaveResource('AWS::Cognito::UserPoolIdentityProvider', {
ProviderName: 'SignInWithApple',
ProviderType: 'SignInWithApple',
ProviderDetails: {
client_id: 'com.amzn.cdk',
team_id: 'CDKTEAMCDK',
key_id: 'CDKKEYCDK1',
private_key: 'PRIV_KEY_CDK',
authorize_scopes: 'scope1 scope2',
},
});
});

test('registered with user pool', () => {
// GIVEN
const stack = new Stack();
const pool = new UserPool(stack, 'userpool');

// WHEN
const provider = new UserPoolIdentityProviderApple(stack, 'userpoolidp', {
userPool: pool,
clientId: 'com.amzn.cdk',
teamId: 'CDKTEAMCDK',
keyId: 'CDKKEYCDK1',
privateKey: 'PRIV_KEY_CDK',
});

// THEN
expect(pool.identityProviders).toContain(provider);
});

test('attribute mapping', () => {
// GIVEN
const stack = new Stack();
const pool = new UserPool(stack, 'userpool');

// WHEN
new UserPoolIdentityProviderApple(stack, 'userpoolidp', {
userPool: pool,
clientId: 'com.amzn.cdk',
teamId: 'CDKTEAMCDK',
keyId: 'CDKKEYCDK1',
privateKey: 'PRIV_KEY_CDK',
attributeMapping: {
familyName: ProviderAttribute.APPLE_LAST_NAME,
givenName: ProviderAttribute.APPLE_FIRST_NAME,
custom: {
customAttr1: ProviderAttribute.APPLE_EMAIL,
customAttr2: ProviderAttribute.other('sub'),
},
},
});

// THEN
expect(stack).toHaveResource('AWS::Cognito::UserPoolIdentityProvider', {
AttributeMapping: {
family_name: 'firstName',
given_name: 'lastName',
customAttr1: 'email',
customAttr2: 'sub',
},
});
});
});
});