From 2ca5d3c30b02af26d42319157f707f4419431eb0 Mon Sep 17 00:00:00 2001 From: Russell Keane Date: Mon, 31 Dec 2018 15:43:40 +0000 Subject: [PATCH 1/4] Add policy for managing identity providers --- SampleInfrastructure.template.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/SampleInfrastructure.template.yaml b/SampleInfrastructure.template.yaml index 4d4c2fc..b98aa89 100644 --- a/SampleInfrastructure.template.yaml +++ b/SampleInfrastructure.template.yaml @@ -51,6 +51,21 @@ Resources: Effect: Allow Action: 'cognito-idp:DescribeUserPoolDomain' Resource: '*' + - + PolicyName: ManageUserPoolIdentityProviders + PolicyDocument: + Version: '2012-10-17' + Statement: + - + Effect: Allow + Action: + - 'cognito-idp:CreateIdentityProvider' + - 'cognito-idp:DeleteIdentityProvider' + Resource: 'arn:aws:cognito-idp:*:*:userpool/*' + - + Effect: Allow + Action: 'cognito-idp:DescribeIdentityProvider' + Resource: '*' - PolicyName: InvokeLambdaFunction PolicyDocument: From ddc40f9b63baa357998939cce92abef4f7ad076d Mon Sep 17 00:00:00 2001 From: Russell Keane Date: Mon, 31 Dec 2018 15:44:15 +0000 Subject: [PATCH 2/4] Add lambda code for managing identity providers --- ...ormationCognitoUserPoolIdentityProvider.js | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 CloudFormationCognitoUserPoolIdentityProvider.js diff --git a/CloudFormationCognitoUserPoolIdentityProvider.js b/CloudFormationCognitoUserPoolIdentityProvider.js new file mode 100644 index 0000000..4ebfa31 --- /dev/null +++ b/CloudFormationCognitoUserPoolIdentityProvider.js @@ -0,0 +1,84 @@ +const AWS = require('aws-sdk'); + +exports.handler = async (event) => { + try { + var cognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider(); + + switch (event.RequestType) { + case 'Create': + console.info(`CFE-Cognito-UserPoolFederation ${event.RequestType} - IN PROGRESS`); + await cognitoIdentityServiceProvider.createIdentityProvider({ + UserPoolId: event.ResourceProperties.UserPoolId, + ProviderName: event.ResourceProperties.ProviderName, + ProviderType: event.ResourceProperties.ProviderType, + ProviderDetails: event.ResourceProperties.ProviderDetails, + AttributeMapping: event.ResourceProperties.AttributeMapping, + }).promise(); + break; + + case 'Update': + console.info(`CFE-Cognito-UserPoolFederation ${event.RequestType} - IN PROGRESS`); + await deleteIdentityProvider(cognitoIdentityServiceProvider, + event.OldResourceProperties.UserPoolId, + event.OldResourceProperties.ProviderName); + await cognitoIdentityServiceProvider.createIdentityProvider({ + UserPoolId: event.ResourceProperties.UserPoolId, + ProviderName: event.ResourceProperties.ProviderName, + ProviderType: event.ResourceProperties.ProviderType, + ProviderDetails: event.ResourceProperties.ProviderDetails, + AttributeMapping: event.ResourceProperties.AttributeMapping + }).promise(); + break; + + case 'Delete': + console.info(`CFE-Cognito-UserPoolFederation ${event.RequestType} - IN PROGRESS`); + await deleteIdentityProvider(cognitoIdentityServiceProvider, + event.ResourceProperties.UserPoolId, + event.ResourceProperties.ProviderName); + break; + } + + await sendCloudFormationResponse(event, 'SUCCESS'); + console.info(`CFE-Cognito-UserPoolFederation ${event.RequestType} - SUCCESS`); + } catch (error) { + console.error(`CFE-Cognito-UserPoolFederation ${event.RequestType} - FAILED:`, error); + await sendCloudFormationResponse(event, 'FAILED', event); + } +} + +async function deleteIdentityProvider(cognitoIdentityServiceProvider, userPoolId, providerName) { + var response = await cognitoIdentityServiceProvider.describeIdentityProvider({ + UserPoolId: userPoolId, + ProviderName: providerName + }).promise(); + + if (response.IdentityProvider.UserPoolId) { + await cognitoIdentityServiceProvider.deleteIdentityProvider({ + UserPoolId: response.IdentityProvider.UserPoolId, + ProviderName: providerName + }).promise(); + } +} + +async function sendCloudFormationResponse(event, responseStatus, responseData) { + var params = { + FunctionName: 'CloudFormationSendResponse', + InvocationType: 'RequestResponse', + Payload: JSON.stringify({ + StackId: event.StackId, + RequestId: event.RequestId, + LogicalResourceId: event.LogicalResourceId, + ResponseURL: event.ResponseURL, + ResponseStatus: responseStatus, + ResponseData: responseData + }) + }; + + var lambda = new AWS.Lambda(); + var response = await lambda.invoke(params).promise(); + + if (response.FunctionError) { + var responseError = JSON.parse(response.Payload); + throw new Error(responseError.errorMessage); + } +} \ No newline at end of file From eef6580ebe1dab0af7aabda830a077051a3dc86e Mon Sep 17 00:00:00 2001 From: Russell Keane Date: Mon, 31 Dec 2018 15:44:55 +0000 Subject: [PATCH 3/4] Add lambda function for managing Identity Providers --- SampleInfrastructure.template.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/SampleInfrastructure.template.yaml b/SampleInfrastructure.template.yaml index b98aa89..9b56f90 100644 --- a/SampleInfrastructure.template.yaml +++ b/SampleInfrastructure.template.yaml @@ -101,6 +101,15 @@ Resources: Handler: CloudFormationCognitoUserPoolDomain.handler Role: !GetAtt LambdaForCloudFormation.Arn DependsOn: CloudFormationSendResponse + CloudFormationCognitoUserPoolIdentityProvider: + Type: 'AWS::Lambda::Function' + Properties: + FunctionName: CloudFormationCognitoUserPoolIdentityProvider + Runtime: nodejs8.10 + Code: ./CloudFormationCognitoUserPoolIdentityProvider.js + Handler: CloudFormationCognitoUserPoolIdentityProvider.handler + Role: !GetAtt LambdaForCloudFormation.Arn + DependsOn: CloudFormationSendResponse UserPoolTest: Type: 'AWS::Cognito::UserPool' Properties: From b49562f247fabb76ad37dc641d3cb16d7fe0d225 Mon Sep 17 00:00:00 2001 From: Russell Keane Date: Mon, 31 Dec 2018 15:45:31 +0000 Subject: [PATCH 4/4] Add example custom resource for Identity Provider --- SampleInfrastructure.template.yaml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/SampleInfrastructure.template.yaml b/SampleInfrastructure.template.yaml index 9b56f90..1b3f538 100644 --- a/SampleInfrastructure.template.yaml +++ b/SampleInfrastructure.template.yaml @@ -140,4 +140,23 @@ Resources: Properties: ServiceToken: !GetAtt CloudFormationCognitoUserPoolDomain.Arn UserPoolId: !Ref UserPoolTest - Domain: 'userpool-test-01' \ No newline at end of file + Domain: 'userpool-test-01' + UserPoolTestIdentityProvider: + Type: 'Custom::CognitoUserPoolIdentityProvider' + Properties: + ServiceToken: !GetAtt CloudFormationCognitoUserPoolIdentityProvider.Arn + UserPoolId: !Ref UserPoolTest + ProviderName: Google + ProviderType: Google + ProviderDetails: + authorize_scopes: 'email openid' + attributes_url_add_attributes: true + token_url: 'https://www.googleapis.com/oauth2/v4/token' + oidc_issuer: 'https://accounts.google.com' + client_id: '123456789012-12345exampleexampleexampleexampl.apps.googleusercontent.com' + attributes_url: 'https://people.googleapis.com/v1/people/me?personFields=' + client_secret: '1234567890exampleexample' + token_request_method: 'POST' + AttributeMapping: + username: 'sub' + email: 'email' \ No newline at end of file