setting up cors on API Gateway with cloudformation
Yesterday I finally (re)-launched ngnr.club and as expected, things didn’t really go as planned. When trying to pull information about a public profile, I was met with CORS errors.link
It was a classic case of “but it worked locally !”. The reason it did work in my dev environemnt is because I was using vite.js
and its proxy
feature, which allowed me to bypass CORS checks. Except the proxy only works when running the dev server, not when building the bundle.
So I had to go back to the drawing board and figure out how to setup CORS on my APIs.
Editing the Method resources
The first step to setup cors on api gateway with cloudformation is to update the method resource with the correct integration/method response information It looks like this:
GetPublicProfileMethod:
Properties:
HttpMethod: GET
ResourceId: !Ref SomeResource
RestApiId: !Ref RestAPI
AuthorizationType: NONE
Integration:
ConnectionType: INTERNET
IntegrationHttpMethod: POST
Type: AWS_PROXY
Uri: <arn of the lambda function>
IntegrationResponses:
- StatusCode: "200"
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent'"
method.response.header.Access-Control-Allow-Origin: "'*'"
method.response.header.Access-Control-Allow-Methods: "'OPTIONS,GET,PUT,POST,DELETE,PATCH,HEAD'"
MethodResponses:
- StatusCode: "200"
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent'"
method.response.header.Access-Control-Allow-Origin: "'*'"
method.response.header.Access-Control-Allow-Methods: "'OPTIONS,GET,PUT,POST,DELETE,PATCH,HEAD'"
Type: AWS::ApiGateway::Method
This will set the correct headers on the response. But we’re not done. We have to update our code to return the same headers with our response. This is an example from my codebase in go:
return events.APIGatewayProxyResponse{
Body: string(body),
Headers: map[string]string{
"content-type": "application/json",
"Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,auth,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,GET,PUT,POST,DELETE,PATCH,HEAD",
},
StatusCode: r.StatusCode,
}
If all you have is unauthenticated endpoints then you’re good to go !
However, if you have authenticated endpoints, it’s going to involve a few more steps. On top of updating our method with the integration/method response, we have to add our custom authorization header
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,my-auth-header,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent'"
For this example, I called it my-auth-header
. This must match the cognito authorizer header that you’ve set on creation. Don’t forget to update your code to include your header.
If you try to make authenticated requests like this, you will still get CORS errors on the browser.
To resolve the issue, we have to create what’s called GatewayResponses
.
Here’s an example:
GatewayResponseDefault4XX:
Type: AWS::ApiGateway::GatewayResponse
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
gatewayresponse.header.Access-Control-Allow-Methods: "'OPTIONS,GET,PUT,POST,DELETE,PATCH,HEAD'"
ResponseType: DEFAULT_4XX
RestApiId: !Ref RestAPI
StatusCode: "200"
GatewayResponseServerError:
Type: AWS::ApiGateway::GatewayResponse
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
gatewayresponse.header.Access-Control-Allow-Methods: "'OPTIONS,GET,PUT,POST,DELETE,PATCH,HEAD'"
ResponseType: DEFAULT_5XX
RestApiId: !Ref RestAPI
StatusCode: "200"
Notice how the status code says 200 ? That’s because if you set it to anything else, the browser will complain that the returned status code isn’t 200.
You can now re-deploy your infrastructure and enjoy an error free experience in production. Don’t forget to re-deploy the API on the API Gateway console for the changes to take effect.
Note: you might want to change the Access-Control-Allow-Origin
header’s value to restrict requests coming from your domain and your domain only.