CloudFormation API Gateway CORS issue - Please help

0

I'm trying to use CloudFormation to create an API Gateway but I'm having CORS error.

Error on the front-end:

POST https://<>.execute-api.us-east-1.amazonaws.com/prod/<> 500
new:1 Access to XMLHttpRequest at '<>' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
  • The API is created without any issue and I even double check every single page on the console against the working API and find no differences in their Method Request, Integration Request, Integration Response and Method Response for all the methods (including the OPTIONS).

  • If I use the Console to Enable CORS for my resources, the API still doesn't work.

  • If I remove the resources created by the template and create them manually using the Console in the same API gateway then my code works as expected. I've tested with the localhost, front-end code in S3 bucket and PostMan, so I can verify that my front-end code, lambda functions and database are working correctly.

I understand that people have had this issue before but I haven't been able to find any answers that solve my issue.

Here's my template:

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "This template creates API Gateway and Lambda function. Version: 1.0.4",

    "Parameters" : {
        "ApiGatewayName": {
            "Description": "Name of the Cognito user pool as a parameter passed into this template.",
            "Type": "String"
        },
        "CognitoUserPoolArn": {
            "Description": "Cognito User Pool ARN passed into this template from root stack.",
            "Type": "String"
        },
        "DynamoDBStack" : {
            "Description": "Name of the stack that created the DynamoDB table for this app, passed as a parameter passed into this template.",
            "Type": "String"
        }
    },

    "Resources": {
        "BaseLambdaExecutionPolicy": {
            "Type": "AWS::IAM::ManagedPolicy",
            "Properties": {
                "Description": "Base permissions needed by all lambda functions.",
                "PolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Action": [
                                "logs:CreateLogGroup",
                                "logs:CreateLogStream",
                                "logs:PutLogEvents"
                            ],
                            "Resource": "*"
                        }
                    ]
                }
            }
        },
        "LambdaRole": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "Service": "lambda.amazonaws.com"
                            },
                            "Action": "sts:AssumeRole"
                        }
                    ]
                },
                "ManagedPolicyArns": [
                    {
                        "Ref": "BaseLambdaExecutionPolicy"
                    }
                ],
                "Policies": [
                    {
                        "PolicyName": {
                            "Fn::Join": ["-", [
                                {"Ref": "ApiGatewayName"}, 
                                "lambda-policy"
                            ]]
                        },
                        "PolicyDocument": {
                            "Version": "2012-10-17",
                            "Statement": [
                                {
                                    "Sid": "",
                                    "Action": [
                                        "dynamodb:DeleteItem",
                                        "dynamodb:GetItem",
                                        "dynamodb:PutItem",
                                        "dynamodb:Query",
                                        "dynamodb:Scan",
                                        "dynamodb:UpdateItem"
                                    ],
                                    "Effect": "Allow",
                                    "Resource": [
                                        { "Fn::ImportValue": {"Fn::Sub": "${DynamoDBStack}-DDBTableARN"}}
                                    ]
                                }
                            ]
                        }
                    }
                ]
            }
        },
        "LambdaFunction": {
            "Type": "AWS::Lambda::Function",
            "Properties": {
                "Role": {
                    "Fn::GetAtt": [
                        "LambdaRole",
                        "Arn"
                    ]
                },
                "Handler": "index.handler",
                "Runtime": "nodejs8.10",
                "Environment": {
                    "Variables": {
                        "TABLE_NAME": {
                            "Fn::ImportValue": {"Fn::Sub": "${DynamoDBStack}-DDBTableName"}
                        }
                    }
                },
                "Code": {
                    "ZipFile": "exports.handler = function(event, context) {}\n"
                }
            }
        },
        "ApiGateway": {
            "Type": "AWS::ApiGateway::RestApi",
            "Properties": {
                "Name": {
                    "Ref": "ApiGatewayName"
                },
                "Description": "A description",
                "FailOnWarnings": true
            }
        },
        "ApiGatewayRole": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "Service": "apigateway.amazonaws.com"
                            },
                            "Action": "sts:AssumeRole"
                        }
                    ]
                },
                "Policies": [
                    {
                        "PolicyName": {
                            "Fn::Join": ["-", [
                                {"Ref": "ApiGatewayName"}, 
                                "api-gateway-policy"
                            ]]
                        },
                        "PolicyDocument": {
                            "Version": "2012-10-17",
                            "Statement": [
                                {
                                    "Effect": "Allow",
                                    "Action": [
                                        "lambda:InvokeFunction"
                                    ],
                                    "Resource": [
                                        {
                                            "Fn::GetAtt": [
                                                "LambdaFunction",
                                                "Arn"
                                            ]
                                        }
                                    ]
                                }
                            ]
                        }
                    }
                ]
            }
        },
        "GatewayAuthorizer": {
            "Type" : "AWS::ApiGateway::Authorizer",
            "Properties" : {
                "AuthorizerCredentials" : {
                    "Fn::GetAtt": ["ApiGatewayRole", "Arn"]
                },
                "IdentitySource" : "method.request.header.Authorization",
                "Name" : {
                    "Fn::Sub": "${ApiGatewayName}-authorizer"
                },
                "ProviderARNs" : [
                    {
                        "Ref": "CognitoUserPoolArn"
                    }
                ],
                "RestApiId" : {
                    "Ref": "ApiGateway"
                },
                "Type" : "COGNITO_USER_POOLS"
            }
        },
        "resourceNotes": {
            "Type" : "AWS::ApiGateway::Resource",
            "Properties" : {
                "ParentId" : {
                    "Fn::GetAtt": ["ApiGateway", "RootResourceId"]
                },
                "PathPart" : "notes",
                "RestApiId" : {"Ref": "ApiGateway"}
            }
        },
        "resourceNoteId": {
            "Type" : "AWS::ApiGateway::Resource",
            "Properties" : {
                "ParentId" : {
                    "Ref": "resourceNotes"
                },
                "PathPart" : "{noteid}",
                "RestApiId" : {"Ref": "ApiGateway"}
            }
        },
        "methodNotesANY": {
            "Type": "AWS::ApiGateway::Method",
            "Properties": {
                "AuthorizationType": "COGNITO_USER_POOLS",
                "AuthorizerId": {"Ref": "GatewayAuthorizer"},
                "RestApiId": {"Ref": "ApiGateway"},
                "ResourceId": {
                    "Ref": "resourceNotes"
                },
                "HttpMethod": "ANY",
                "Integration": {
                    "Type": "AWS_PROXY",
                    "IntegrationHttpMethod": "ANY",
                    "Uri": {"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations"},
                    "IntegrationResponses": [{
                        "StatusCode": "200"
                    }]
                },
                "RequestParameters": {
                    "method.request.header.Access-Control-Allow-Origin": "'*'"
                },
                "MethodResponses": [{
                    "ResponseModels": {
                    "application/json": "Empty"
                    },
                    "StatusCode": "200"
                }]
            }
        },
        "methodNotesOPTIONS": {
            "Description": "Enable CORS for resource.",
            "Type": "AWS::ApiGateway::Method",
            "Properties": {
                "AuthorizationType": "NONE",
                "RestApiId": {"Ref": "ApiGateway"},
                "ResourceId": {
                    "Ref": "resourceNotes"
                },
                "HttpMethod": "OPTIONS",
                "Integration": {
                    "Type": "MOCK",
                    "IntegrationResponses": [
                        {
                            "ResponseParameters": {
                                "method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
                                "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'",
                                "method.response.header.Access-Control-Allow-Origin": "'*'"
                            },
                            "ResponseTemplates": {
                                "application/json": ""
                            },
                            "StatusCode": "200"
                        }
                    ],
                    "PassthroughBehavior": "WHEN_NO_MATCH",
                    "RequestTemplates": {
                        "application/json": "{\"statusCode\": 200}"
                    }
                },
                "MethodResponses": [
                    {
                        "ResponseModels": {
                            "application/json": "Empty"
                        },
                        "ResponseParameters": {
                            "method.response.header.Access-Control-Allow-Headers": false,
                            "method.response.header.Access-Control-Allow-Methods": false,
                            "method.response.header.Access-Control-Allow-Origin": false
                        },
                        "StatusCode": "200"
                    }
                ]
            }
        },
        "methodNoteIdANY": {
            "Type": "AWS::ApiGateway::Method",
            "Properties": {
                "AuthorizationType": "COGNITO_USER_POOLS",
                "AuthorizerId": {"Ref": "GatewayAuthorizer"},
                "RestApiId": {"Ref": "ApiGateway"},
                "ResourceId": {
                    "Ref": "resourceNoteId"
                },
                "HttpMethod": "ANY",
                "Integration": {
                    "Type": "AWS_PROXY",
                    "IntegrationHttpMethod": "ANY",
                    "Uri": {"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations"},
                    "IntegrationResponses": [{
                        "StatusCode": "200"
                    }]
                },
                "RequestParameters": {
                    "method.request.header.Access-Control-Allow-Origin": "'*'"
                },
                "MethodResponses": [{
                    "ResponseModels": {
                    "application/json": "Empty"
                    },
                    "StatusCode": "200"
                }]
            }
        },
        "methodNoteIdOPTIONS": {
            "Description": "Enable CORS for resource.",
            "Type": "AWS::ApiGateway::Method",
            "Properties": {
                "AuthorizationType": "NONE",
                "RestApiId": {"Ref": "ApiGateway"},
                "ResourceId": {
                    "Ref": "resourceNoteId"
                },
                "HttpMethod": "OPTIONS",
                "Integration": {
                    "Type": "MOCK",
                    "IntegrationResponses": [
                        {
                            "ResponseParameters": {
                                "method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
                                "method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'",
                                "method.response.header.Access-Control-Allow-Origin": "'*'"
                            },
                            "ResponseTemplates": {
                                "application/json": ""
                            },
                            "StatusCode": "200"
                        }
                    ],
                    "PassthroughBehavior": "WHEN_NO_MATCH",
                    "RequestTemplates": {
                        "application/json": "{\"statusCode\": 200}"
                    }
                },
                "MethodResponses": [
                    {
                        "ResponseModels": {
                            "application/json": "Empty"
                        },
                        "ResponseParameters": {
                            "method.response.header.Access-Control-Allow-Headers": false,
                            "method.response.header.Access-Control-Allow-Methods": false,
                            "method.response.header.Access-Control-Allow-Origin": false
                        },
                        "StatusCode": "200"
                    }
                ]
            }
        },
        "ApiGatewayDeployment": {
            "Type": "AWS::ApiGateway::Deployment",
            "DependsOn": ["methodNotesANY", "methodNoteIdANY"],
            "Properties": {
                "RestApiId": {
                    "Ref": "ApiGateway"
                },
                "StageName": "prod"
            }
        }
    },

    "Outputs": {
        "ApiGatewayProdInvokeURL": {
            "Value": {
                "Fn::Sub": "https://${ApiGateway}.execute-api.${AWS::Region}.amazonaws.com/prod"
            },
            "Description": "API Gateway prod Invoke URL."
        },
        "ApiGatewayId": {
            "Value": {"Ref": "ApiGateway"}
        }
    }
}

Please note that the "method.response.header.Access-Control-Allow-Origin": false actually creates the API with the same settings as the working one.

I also use the code in the correct answer here: https://stackoverflow.com/questions/40292888/enable-cors-for-api-gateway-in-cloudformation-template .

Yes, my OPTIONS request has the "Access-Control-Allow-Origin" header.

I'm sorry for the long post but I really need help with this. I've looked everywhere and I don't have much luck (or love) on StackOverflow.

Edited by: awsuser29 on Jun 24, 2019 1:25 PM

Edited by: awsuser29 on Jun 24, 2019 1:25 PM

Edited by: awsuser29 on Jun 26, 2019 8:22 AM

asked 4 years ago676 views
1 Answer

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions