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

已提问 5 年前1766 查看次数
1 回答
1
已回答 5 年前

您未登录。 登录 发布回答。

一个好的回答可以清楚地解答问题和提供建设性反馈,并能促进提问者的职业发展。

回答问题的准则