Reputation: 4250
I'm trying to deploy an API in API Gateway using cloudformation. The methods require CORS to be enabled, I followed the template here Enable CORS for API Gateway in Cloudformation template to do so. Here is my template
AuthorizerRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "sts:AssumeRole"
Effect: "Allow"
Principal:
Service:
- "apigateway.amazonaws.com"
Policies:
- PolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "lambda:invokeFunction"
Effect: "Allow"
Resource:
- !GetAtt "MyAPIAuthorizer.Arn"
PolicyName: "lambda"
Authorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
AuthorizerResultTtlInSeconds: 0
AuthorizerCredentials: !GetAtt "AuthorizerRole.Arn"
AuthorizerUri:
Fn::Join:
- ""
-
- "arn:aws:apigateway:"
- Ref: "AWS::Region"
- ":lambda:path/2015-03-31/functions/"
- Fn::GetAtt:
- "MyAPIAuthorizer"
- "Arn"
- "/invocations"
Type: "TOKEN"
IdentitySource: "method.request.header.token"
Name: "DefaultAuthorizer"
RestApiId: !Ref RestApi
MyAPIAuthorizer:
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket: my-My-lambda-us-east-1
S3Key: node_lambdas.zip
Handler: My-APIAuthorizer.handler
Role: !Ref Role
Runtime: nodejs6.10
Timeout: 300
VpcConfig:
SecurityGroupIds:
- !Ref SecurityGroup
SubnetIds: !Ref Subnets
MyAuthenticateUser:
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket: My-My-lambda-us-east-1
S3Key: node_lambdas.zip
Handler: My-AuthenticateUser.handler
Role: !Ref Role
Runtime: nodejs6.10
Timeout: 300
VpcConfig:
SecurityGroupIds:
- !Ref SecurityGroup
SubnetIds: !Ref Subnets
#Policies: AWSLambdaDynamoDBExecutionRole
MyAuthenticateUserApiGatewayInvoke:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt "MyAuthenticateUser.Arn"
Principal: "apigateway.amazonaws.com"
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/*/*/*"
MyAuthenticateUserResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref RestApi
ParentId: !Ref ApiResourceParent
PathPart: authenticateuser
MyAuthenticateUserPost:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref RestApi
ResourceId: !Ref MyAuthenticateUserResource
HttpMethod: POST
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: POST
Type: AWS
Uri: !Sub
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations"
- lambdaArn: !GetAtt "MyAuthenticateUser.Arn"
IntegrationResponses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
MethodResponses:
- StatusCode: 200
ResponseModels:
application/json: 'Empty'
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: true
MyAuthenticateUserOptions:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref RestApi
ResourceId: !Ref MyAuthenticateUserResource
HttpMethod: OPTIONS
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: POST
IntegrationResponses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,token'"
method.response.header.Access-Control-Allow-Methods: "'POST,OPTIONS'"
method.response.header.Access-Control-Allow-Origin: "'*'"
Type: MOCK
MethodResponses:
- StatusCode: 200
ResponseModels:
application/json: 'Empty'
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: true
method.response.header.Access-Control-Allow-Methods: true
method.response.header.Access-Control-Allow-Origin: true
MyFunction:
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket: My-My-lambda-us-east-1
S3Key: node_lambdas.zip
Handler: My-Function.handler
Role: !Ref Role
Runtime: nodejs6.10
Timeout: 300
VpcConfig:
SecurityGroupIds:
- !Ref SecurityGroup
SubnetIds: !Ref Subnets
#Policies: AWSLambdaDynamoDBExecutionRole
MyFunctionApiGatewayInvoke:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunction
FunctionName: !GetAtt "MyFunction.Arn"
Principal: "apigateway.amazonaws.com"
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/*/*/*"
MyFunctionResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref RestApi
ParentId: !Ref ApiResourceParent
PathPart: Function
MyFunctionGet:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref RestApi
ResourceId: !Ref MyFunctionResource
HttpMethod: GET
AuthorizationType: CUSTOM
AuthorizerId: !Ref Authorizer
Integration:
IntegrationHttpMethod: GET
Type: AWS
Uri: !Sub
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations"
- lambdaArn: !GetAtt "MyFunction.Arn"
IntegrationResponses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
MethodResponses:
- StatusCode: 200
ResponseModels:
application/json: 'Empty'
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: true
MyFunctionOptions:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref RestApi
ResourceId: !Ref MyFunctionResource
HttpMethod: OPTIONS
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: GET
IntegrationResponses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,token'"
method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'"
method.response.header.Access-Control-Allow-Origin: "'*'"
Type: MOCK
MethodResponses:
- StatusCode: 200
ResponseModels:
application/json: 'Empty'
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: true
method.response.header.Access-Control-Allow-Methods: true
method.response.header.Access-Control-Allow-Origin: true
After deploying the API the MyAuthenticateUserPost
method returns a 200 with the following response headers
Access-Control-Allow-Origin →*
Connection →keep-alive
Content-Length→249
Content-Type →application/json
Date →Fri, 28 Sep 2018 21:15:38 GMT
Via →1.1 sdlkfnsdlk.cloudfront.net(CloudFront)
X-Amz-Cf-Id→dflknsdlfkn
X-Amzn-Trace-Id →Root=sdlkfnsdlk;Sampled=0
X-Cache →Miss from cloudfront
x-amz-apigw-id →sdklfnsdlk
x-amzn-RequestId →slkfnlsdk
but the MyFunctionGet
method returns a 500 with the following response headers
Connection →keep-alive
Content-Length →36
Content-Type →application/json
Date →Fri, 28 Sep 2018 21:19:04 GMT
Via →1.1 slkdfnk.cloudfront.net (CloudFront)
X-Amz-Cf-Id →dsklfnsdlk
X-Cache →Error from cloudfront
x-amz-apigw-id →dlsfknsdlkfn
x-amzn-RequestId →sdkfnsdkln
The 500 response is missing the Access-Control-Allow-Origin
and X-Amzn-Trace-Id
headers. The difference between the 2 methods is that the working method is a POST and has no authorization while the one that doesn't work is a GET and has a custom Authorizer. I can make the method returning a 500 work if I go into the API Gateway console, select the GET method->Integration Request, and save the Lambda Function like so
That function is already present in that field after the cloudformation deploy, and I've added the permission in the template, but the API Gateway method will not work unless I perform this manual step. I have about 50 methods so I would like to completely automate this. Am I missing something in my template?
Update: In response to @jny I updated the integration responses in my Get method like so
IntegrationResponses:
- StatusCode: 200
SelectionPattern: "2\\{d}2"
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
- StatusCode: 300
SelectionPattern: "3\\{d}2"
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
- StatusCode: 400
SelectionPattern: "4\\{d}2"
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
- StatusCode: 500
SelectionPattern: "5\\{d}2"
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
MethodResponses:
- StatusCode: 200
ResponseModels:
application/json: 'Empty'
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: true
- StatusCode: 300
ResponseModels:
application/json: 'Empty'
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: true
- StatusCode: 400
ResponseModels:
application/json: 'Empty'
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: true
- StatusCode: 500
ResponseModels:
application/json: 'Empty'
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: true
I also made the same update to my options method
IntegrationResponses:
- StatusCode: 200
SelectionPattern: "2\\{d}2"
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,token'"
method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'"
method.response.header.Access-Control-Allow-Origin: "'*'"
- StatusCode: 300
SelectionPattern: "3\\{d}2"
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,token'"
method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'"
method.response.header.Access-Control-Allow-Origin: "'*'"
- StatusCode: 400
SelectionPattern: "4\\{d}2"
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,token'"
method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'"
method.response.header.Access-Control-Allow-Origin: "'*'"
- StatusCode: 500
SelectionPattern: "5\\{d}2"
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,token'"
method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'"
method.response.header.Access-Control-Allow-Origin: "'*'"
MethodResponses:
- StatusCode: 200
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: 300
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: 400
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: 500
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
I still see the 500 response when invoking the API method
Upvotes: 0
Views: 3239
Reputation: 8067
You have to configure ResponseParameters for all statuses, not just 200.
Something like that:
"IntegrationResponses": [
{
"ResponseParameters":{
"method.response.header.Access-Control-Allow-Origin": "'*'"
},
"StatusCode": 200,
"ResponseTemplates": {
....
}
},
{
"StatusCode": 500,
"SelectionPattern": "5\\{d}2",
"ResponseTemplates": {
....
}
}
],
and same for method responses, e.g.:
"MethodResponses": [{
"ResponseModels": {
"application/json": "Empty"
},
"ResponseParameters":{
"method.response.header.Access-Control-Allow-Origin": "'*'"
},
"StatusCode": "200"
},
{
"StatusCode": "500"
}
]
Upvotes: 0