rosberglinhares / CloudFormationCognitoCustomResources

CognitoUserPoolClientSettings and CognitoUserPoolDomain custom resources for AWS CloudFormation.
MIT License
108 stars 34 forks source link

Adding Federated Identities to User Pool #3

Open junaway opened 6 years ago

junaway commented 6 years ago

Here's my extension to handle Federated Identities right into User Pools.

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);
    }
}

The required Lambda Role is:

CFECognitoRole:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: CFE-Cognito-Role
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Action: 'sts:AssumeRole'
          Principal:
            Service: lambda.amazonaws.com
      Policies:
      - PolicyName: CFE-Cognito-WriteCloudWatchLogs
        PolicyDocument: 
          Version: '2012-10-17'
          Statement: 
          - Effect: Allow
            Action:
              - 'logs:CreateLogGroup'
              - 'logs:DeleteLogGroup'
              - 'logs:CreateLogStream'
              - 'logs:DeleteLogStream'
              - 'logs:PutLogEvents'
            Resource: 'arn:aws:logs:*:*:*'
      - PolicyName: CFE-Cognito-InvokeLambdaFunction
        PolicyDocument: 
          Version: '2012-10-17'
          Statement: 
          - Effect: Allow
            Action: 'lambda:InvokeFunction'
            Resource: 'arn:aws:lambda:*:*:function:*'
      - PolicyName: CFE-Cognito-ManageUserPoolIdentityProvider
        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: '*'

There. Didn't bother making a PR, sorry.

Russ-K commented 5 years ago

@juanpablovega / @rosberglinhares - I've created a PR for this including an example custom resource for a Google Identity Provider.

See #8