The problem
Setting CORS while using AWS API Gateway can be confusing as the Enable CORS option in the Action menu doesn't work out of the box. The problem occurs during the pre-flight call that fail as it is. A solution to this issue is simply to create a MOCK endpoint for such API and make sure the response headers are set appropriately.
Mock integrations are a quick and easy way to enable CORS
A mock integration can be implemented either on the console or better yet using a Cloudformation template.
Header mappings via console
These are some simple steps (far from perfect) I have used to enable CORS using the console
1. Add the OPTIONS method to each endpoint. Configure the endpoint as a MOCK integration.
2. In the Method Response (Left bottom) block add Status 200 and the corresponding Response Headers
Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin
3. In the Method Execution (Right Bottom) block, use the 200 response status created in step 2 and map headers as shown below.
Access-Control-Allow-Headers: 'Access-Control-Allow-Origin, Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'
Access-Control-Allow-Methods: 'OPTIONS'
Access-Control-Allow-Origin: '*'
Mapping Template: application/json
{
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*"
},
body: ''
};
Some sources advise explicitly setting the header in the mapping template too.
4. Save your changes and deploy API using the actions menu.
As mentioned in step 1, this should be repeated for each CORS-enabled endpoint, that includes GET requests.
You also need to add the Access-Control-Allow-Origin: '*'
mapping to your GET, POST, PUT, DELETE methods (steps 2, 3)
Add more response codes (4xx, 5xx) by repeating steps 2 and 3 🔄
A better way: Cloudformation
A more IaaC-compliant way to achieve this is by writing some good old yaml as a Cloudformation template that can be use to define an integration for the specific resource. The definition is for the OPTIONS method and include status code, the response parameters (specifically the allow origin header), and the Mock
type. The example below requires identifiers for the the API and the Resource and allows the POST method only.
OptionsMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
RestApiId:
Ref: ApiIdentifier
ResourceId:
Ref: ResourceIdentifier
HttpMethod: OPTIONS
Integration:
IntegrationResponses:
- StatusCode: 200
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: "'POST,OPTIONS'"
method.response.header.Access-Control-Allow-Origin: "'*'"
ResponseTemplates:
application/json: ''
PassthroughBehavior: WHEN_NO_MATCH
RequestTemplates:
application/json: '{"statusCode": 200}'
Type: MOCK
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
The last three lines in this yaml specify whether the headers are required.
It seems a bit over complicated way to achieve something this simple but at least it seems consistent with the way API Gateway works elsewhere.