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

1개 답변
1
답변함 5년 전

로그인하지 않았습니다. 로그인해야 답변을 게시할 수 있습니다.

좋은 답변은 질문에 명확하게 답하고 건설적인 피드백을 제공하며 질문자의 전문적인 성장을 장려합니다.

질문 답변하기에 대한 가이드라인

관련 콘텐츠