One thing that I like about serverless.com is that CORS can be enabled for an endpoint in one short line:
cors: true
Currently, to enable CORS using x-amazon-apigateway-integration, it is necessary to:
responses:
'200':
description: Request successful
headers:
Access-Control-Allow-Origin:
type: string
schema:
$ref: '#/definitions/Success'
'400':
description: Authentication failed
headers:
Access-Control-Allow-Origin:
type: string
schema:
$ref: '#/definitions/Error'
x-amazon-apigateway-integration:default:
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Origin : "'*'"
'.*"status":400.*':
statusCode: '400'
responseParameters:
method.response.header.Access-Control-Allow-Origin : "'*'"
options section:options:
summary: CORS support
description: |
Enable CORS by returning correct headers
responses:
200:
description: Default response for CORS method
headers:
Access-Control-Allow-Headers:
type: string
Access-Control-Allow-Methods:
type: string
Access-Control-Allow-Origin:
type: string
x-amazon-apigateway-integration:
type: mock
requestTemplates:
application/json: |
{"statusCode" : 200}
responses:
"default":
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Headers : "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'"
method.response.header.Access-Control-Allow-Methods : "'*'"
method.response.header.Access-Control-Allow-Origin : "'*'"
responseTemplates:
application/json: |
{}
...it'd be great if Transform: AWS::Serverless-2016-10-31 could transform the swagger schema, automatically adding all of this if cors: true is present in the CloudFormation object
I like this. This would be doable for implicit APIs specified in the Events section of a Function resource, because CloudFormation generates the swagger for you. For APIs created using AWS::Serverless::API resource, users will supply their own Swagger file in S3. The logic that parses Serverless resources does not have the access to read and manipulate S3 files.
Does the former allow you to save a local copy of the swagger schema, so that we may merge/cross-check the generated and documenting swagger schema?
Yeah. After you create the stack you can download the generated template using CloudFormation get-template command. It will have the Swagger template embedded in it
This could be somewhat DRYed by defining a patterned object at the root of the Swagger file:
x-amazon-apigateway-integration-cors-headers:
method.response.header.Access-Control-Allow-Methods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'"
method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Origin: "'*'"
After that, these headers can be referenced in x-amazon-apigateway-integration: through
responseParameters:
allOf:
- $ref: '#/x-amazon-apigateway-integration-cors-headers'
Just confirming that for this feature request, we're talking about entirely eliminating the need for users to create their own Swagger files to enable CORS. We shouldn't force users to explicitly adopt Swagger if they don't want to. (It's fine if a Swagger file is autogenerated under the covers, as long as users don't have to manually create a Swagger file.)
Personally, I'm a fan of Swagger, but x-amazon-apigateway-integration with CORS adds too much noise.
It'd be nice to keep an option to specify a Swagger template, but without all the extra clutter. One case we need to use Swagger for is for input validation, where we'd like to make sure the shape of the API is preserved between that defined in a SAM template and that specified explicitly in Swagger (with the latter having extra information about input parameter types etc.)
Could there be an option to define a "template template" Swagger, with _all_ of API Gateway integration parameters specified over a ${SOME_VAR} syntax that are then dynamically expanded during processing of the SAM template? This will be then very similar to how currently the SAM template is itself "expanded" into a normal CloudFormation template during packaging.
To echo what @rabowskyb said, it would be nice to have a cors: true option in the sam.yml file itself. I'm using SAM to build a lot of microservices and a swagger file seems overkill for something that literally only has one endpoint. [edit: to be fair, I haven't spent any time trying to learn swagger, so it may not be all that difficult... but I still think a SAM-only version would be much appreciated]
Here is a proposal of how this could work. API Event will get a new property called Cors. Here are two ways to use it:
Type: AWS::Serverless::Function
Properties:
Runtime: ...
Events:
GetApi:
Type: Api
Properties:
Cors: "*"
Method: GET
Path: /foo
PostApi:
Type: Api
Properties:
Cors: "http://example.com"
Method: POST
Path: /foo
This will generate CORS headers automatically, for each Path+Method combo and set Allow-Origin to "*". The value of Cors property is a list of origins you want to allow. Allow-Headers is always set to "*".
Type: AWS::Serverless::Function
Properties:
Runtime: ...
Events:
GetApi:
Type: Api
Properties:
Cors:
AllowOrigin: "List of origins"
AllowHeaders: "List of allowed headers"
Method: GET
Path: /foo
PostApi:
Type: Api
Properties:
Cors:
AllowOrigin: "List of origins"
AllowHeaders: "List of allowed headers"
Method: POST
Path: /foo
This will generate CORS headers automatically, for each Path+Method combo and set Allow-Origin and Allow-Headers to whatever value that was specified.
Would this address use-cases you guys are going after?
Unfortunately we can't add the same functionality to AWS::Serverless::Api property because the translator converting SAM to CloudFormation cannot read and modify your Swagger file.
So both the short form and long form will be offered? Would it be possible to allow use of wildcards in the list of origins, for example "*.example.com"?
Overall, your proposal above appears to eliminate the need to use swagger to enable CORS, while providing a degree of flexibility to specify allowed origins and headers. This supports my use case of webapps backed by serverless backends deployed with SAM.
yes, both forms will be available. SAM wouldn't touch the value of AllowOrigin you specify. So you can write anything that HTTP will accept
Is there any ETA for this feature? This will make the use of SAM so much easier...
Any updates really want this feature?
Did a comparison between serverless framework and SAM and the only reason serverless has less lines in yaml is due to the verbose inline swagger definitions.
Is there any update regarding this ... i think most of us are desperately waiting for this feature.
May we also ask to be able to specify allowed methods per-resource? E.g. some resources may only have Get or only Post, while others a broader spectrum (e.g. Get, Patch, and Delete). It wouldn't make sense to return all "legal" verbs at once API-wide when only some of them are implemented, depending on resource. Unfortunately, doing that is common in other frameworks (e.g. Express), even though it goes against the RFC and the intended purpose of the Options request.
@dinvlad we can already infer the method name from the API definition and apply Cors only on it. Doable.
As for ETA, I don't have a concrete date yet. We have started the implementation. So this is definitely happening :-)
Awesome, great to hear Cors support is on the roadmap. Any further updates on ETA?
+1 Any updates maybe? or at least an example how to do this in SAM before it is implemented
Quick update: We are almost done with the implementation. We will rollout this change shortly after QA. A couple more weeks, may be. Hang tight :-)
Any update @sanathkr ?
We've implemented a swagger file to get around for the time being but are keen to remove it.
@sanathkr in the proposal, you mention a _list_ of origins. Does that mean SAM will enable dynamic generation of Access-Control-Allow-Origin based on the Origin of request? Or will it just set it to a single static value?
Any updates on this? As it happens, I need it desperately :)
Any update on ETA for this feature?
Are there any updates?
Any updates on this? It would be such a wonderful feature to have.
The Lambda team did an AMA on Reddit, and someone asked about updates on CORS. Here's their response:
I hear you. There are times when we have to make tradeoffs on getting one feature out before the other. We are almost done with implementing the CORS feature now. So hang tight, it will be out real soon - Sanath
Thanks for the update @frehner ! Appreciate it!
any updates on this? Thanks!
Also looking for an update... Sounds like it was going to be just a couple of weeks back in July.
+1 to this feature request. It's not SO bad to do it other ways but it's a lot of detail when this transform allows you to otherwise be so succinct.
+1 This is so damn frustrating. Been stuck on configuring CORS for an eternity! I feel lost in a rabbit hole of poo.
With more and more applications being written in a js framework I would think that hosting a static site on S3 and speaking to a lambda function via api gateway would be well documented by now?
Will await this feature deploy and continue searching the web for a workaround.
+1
Hey guys, apologies for the delay. We had a context switch to get safe Lambda deployment capabilites in SAM.
We know how important this feature is to all of you. It is definitely one of the top items on our roadmap. We are currently exploring options to implement in a manner that will work for both Implicit APIs & Explicit APIs (ie. where you provide your Swagger). I will keep the thread engaged as we have more developments.
Meanwhile here is the example file that tells you how to enable CORS yourself if you use Swagger - https://github.com/awslabs/serverless-application-model/tree/master/examples/2016-10-31/api_swagger_cors
There was a comment (now deleted) asking for workarounds, after the comment from sanathkr.
For others who are struggling with this, here's how I addressed this issue. I have an "implicit" API, meaning that I have only one path in my Swagger file that routes to a single Lambda, and then the Lambda reacts differently based on the request method and path. I think this would be generalizable to explicit APIs or non-Lambda backends, as well.
If you know CORS very well some of this is probably remedial for you, but it's here for those who don't.
The key is that you must understand there are two categories of request in CORS: simple and pre-flighted (Good read here.) A simple request is a GET, POST, or HEAD, with certain content types. These will "just work" as long as the response has the right CORS access control headers.
All other request types are subject to "pre-flight check", which involves the browser sending an OPTIONS request to the server and checking the response for the CORS access control headers before allowing any other requests your client-side code wants to make. _Then_, assuming the headers check out, the browser will make your real requests.
The upshot of this is that you may have to handle the two cases individually.
In my case, I chose to write the CORS access control headers in two places: in my Swagger definition _and_ in my server-side (Lambda) code, to cover pre-flight and simple cases, respectively. So:
You have to ensure that responses have the appropriate access control headers. I believe you could enumerate response conditions in the x-amazon-apigateway-integration/responses section of Swagger to have the API Gateway write the headers for you. I haven't gotten around to that yet, and I may not at all. Instead, I chose to write these headers in my server-side code. Here's a snippet from my work, which happens to be in C# for this project:
// do stuff to get an APIGatewayProxyResponse
// Enabling CORS for non-preflighted (simple) requests.
if (response.Headers == null)
response.Headers = new Dictionary<string, string>();
response.Headers["Access-Control-Allow-Methods"] = "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT";
response.Headers["Access-Control-Allow-Headers"] = "Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token";
response.Headers["Access-Control-Allow-Origin"] = "*";
return response;
Easy peasy, and my Swagger API definiton remains extremely short and easy to read/manage. _If_ I were to write my Lambda to handle OPTIONS requests as well, I could probably solve the problem entirely in server-side code. However, I would prefer to avoid paying for Lambda invocations for pre-flights (and I kinda regard this as a hack until this issue is resolved), so I did the next step.
This is where the example linked above comes in more handy. It's a lot to look at, so here's the part you probably want from the Swagger definition:
options:
consumes:
- application/json
produces:
- application/json
responses:
'200':
description: 200 response
schema:
$ref: "#/definitions/Empty"
headers:
Access-Control-Allow-Origin:
type: string
Access-Control-Allow-Methods:
type: string
Access-Control-Allow-Headers:
type: string
x-amazon-apigateway-integration:
responses:
default:
statusCode: 200
responseParameters:
method.response.header.Access-Control-Allow-Methods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'"
method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Origin: "'*'"
passthroughBehavior: when_no_match
requestTemplates:
application/json: "{\"statusCode\": 200}"
type: mock
This creates a mock API just for the OPTIONS method that always returns a 200 with the CORS headers. You probably want this for all the items in your paths section. So for example, my Swagger definition now looks like this:
paths:
/:
options:
[ The mock API above ]
/{proxy+}:
options:
[ The mock API above ]
x-amazon-apigateway-any-method:
[ My Lambda backend integration]
If you have a more explicit API without a "greedy" proxy like I did, you may have to duplicate the options mock many times. In that case I would refer to the DRY comment above to reduce line count on your Swagger definition somewhat.
YMMV. I reserve the right to be wrong or dumb about this, and I am sure if I am someone will promptly tell me. I therefore reserve the right to edit this comment to be more helpful or make me look less dumb as needed. Good luck.
We created a parent task to track this. Please see #248 for updates.
Closing as duplicate of #248
Just in case it might be of some use, here's an example of SAM with CORS enabled:
https://github.com/aws-samples/startup-kit-serverless-workload.
The example by 'ghost' above works for me but now I get 401's as I have configured a default authorizer. However, this should only be applied for the non-OPTIONS methods. For the OPTIONS, I want authorizer NONE. Anyone got a clue how to get it working ?
Edit: I found it
Adding
security:
- NONE: []
to the 'OPTIONS' section in the swagger body has the intended effect. Hope it can help someone..
Most helpful comment
Here is a proposal of how this could work. API Event will get a new property called
Cors. Here are two ways to use it:Shorthand
This will generate CORS headers automatically, for each Path+Method combo and set Allow-Origin to "*". The value of Cors property is a list of origins you want to allow. Allow-Headers is always set to "*".
Long Form
This will generate CORS headers automatically, for each Path+Method combo and set Allow-Origin and Allow-Headers to whatever value that was specified.
Would this address use-cases you guys are going after?
Unfortunately we can't add the same functionality to
AWS::Serverless::Apiproperty because the translator converting SAM to CloudFormation cannot read and modify your Swagger file.