aws / aws/serverless-application-model

AWS::Serverless::Api does not correctly support multiple CORS Origins

Open
#3,769 6 comments 4 reactions 0 assignees View on GitHub
stage/needs-triage
Dominant language
Python
Stars
9.6k
Forks
2.5k
Avg merge
1d 11h
Merged PRs (30d)
7

Description

The [`AWS::Serverless::Api`](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html) [`CorsConfiguration`](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-api-corsconfiguration.html#sam-api-corsconfiguration-alloworigin) has an [`AllowOrigin`](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-api-corsconfiguration.html#sam-api-corsconfiguration-alloworigin) property that purports to allow multiple origins to be configured for the CORS `Access-Control-Allow-Origin` header for preflight requests using `OPTION`:

> `AllowOrigin`
> String of origin to allow. This can be a comma-separated list in string format.

However the actual HTTP `Access-Control-Allow-Origin` header does not permit multiple comma-separated domains as a value. See the [MDN docs for `Access-Control-Allow-Origin`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Origin). See also the [actual, living spec](https://fetch.spec.whatwg.org/#http-responses). (One [answer on Stack Overflow](https://stackoverflow.com/a/12818477) indicated that an earlier version of the spec might have allowed a list many years ago, but that is not currently the case and moreover browsers don't support it.)

It is possible for a server to allow multiple origins by _dynamically_ looking at the requested `Origin` in a preflight request and echoing back the requested `Origin` if it matches a known list, as discussed in the answers to [Access-Control-Allow-Origin Multiple Origin Domains?](https://stackoverflow.com/q/1653308). And in fact according to the AWS Compute Blog article [Configuring CORS on Amazon API Gateway APIs](https://aws.amazon.com/blogs/compute/configuring-cors-on-amazon-api-gateway-apis/), the SAM support for _HTTP APIs_ does exactly that (while allowing an actual YAML list in the template as well):

> Note that the **AllowOrigin** section allows more than one domain. When the browser makes a request, HTTP APIs checks the list for the incoming origin. If it exists, HTTP APIs adds it to the **access-control-allow-origin** header in the response.

However for REST APIs, SAM simply copies the comma-separate list. See for example this JSON transformed from a SAM template using `AllowOrigin: "'http://localhost,https://example.com'"`:

```json
"x-amazon-apigateway-integration": {
"type": "mock",
"requestTemplates": {
"application/json": "{\n \"statusCode\" : 200\n}\n"
},
"responses": {
"default": {
"statusCode": "200",
"responseTemplates": {
"application/json": "{}\n"
},
"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-Origin": "'http://localhost,https://example.com'",
"method.response.header.Access-Control-Allow-Methods": "'POST, GET'",
"method.response.header.Access-Control-Allow-Credentials": "'true'"
}
}
}
},
"summary": "CORS support",
```

Because `http://localhost,https://example.com` is not an allowed value for `Access-Control-Allow-Origin`, the browser won't match it; more accurately, it likely considers the entire string as a single domain, so it won't match a request from `http://localhost` for example.

What SAM should do is parse out the comma-separated list and provide some `OPTIONS` implementation that checks the request and echoes back the requested `Origin` if it matches an origin in the list from the template (as the SAM HTTP API purportedly does, which I haven't tested). This would be the best approach. Note that if this is done, a `Vary: Origin` will need to be added to the responses.

An alternative is to prevent multiple origins altogether for REST APIs (updating the documentation appropriately to remove the description of a comma-separated list of values).

Leaving things as they are is incorrect and simply doesn't work: browsers won't properly match a list of origins in the `Access-Control-Allow-Origin` header value.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.