Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cors: true #23

Closed
nalbion opened this issue Nov 24, 2016 · 37 comments
Closed

cors: true #23

nalbion opened this issue Nov 24, 2016 · 37 comments

Comments

@nalbion
Copy link

nalbion commented Nov 24, 2016

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:

  • add a header to each response:
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'
  • add responseParameters in 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 : "'*'"
  • add a massive 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

@sanathkr
Copy link
Contributor

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.

@nalbion
Copy link
Author

nalbion commented Nov 28, 2016

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?

@sanathkr
Copy link
Contributor

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

@dinvlad
Copy link

dinvlad commented Nov 30, 2016

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'

@rabowskyb
Copy link
Contributor

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.)

@nalbion
Copy link
Author

nalbion commented Jan 10, 2017

Personally, I'm a fan of Swagger, but x-amazon-apigateway-integration with CORS adds too much noise.

@dinvlad
Copy link

dinvlad commented Jan 10, 2017

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.

@frehner
Copy link

frehner commented Jan 31, 2017

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]

@sanathkr
Copy link
Contributor

sanathkr commented Feb 3, 2017

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

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 "*".

Long Form

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.

@rabowskyb
Copy link
Contributor

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.

@sanathkr
Copy link
Contributor

sanathkr commented Feb 4, 2017

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

@hoegertn
Copy link

Is there any ETA for this feature? This will make the use of SAM so much easier...

@gertjvr
Copy link

gertjvr commented Mar 21, 2017

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.

@ahmadsn26
Copy link

Is there any update regarding this ... i think most of us are desperately waiting for this feature.

@dinvlad
Copy link

dinvlad commented Jun 14, 2017

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.

@sanathkr
Copy link
Contributor

@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 :-)

@john
Copy link

john commented Jul 11, 2017

Awesome, great to hear Cors support is on the roadmap. Any further updates on ETA?

@lskrajny
Copy link

lskrajny commented Jul 26, 2017

+1 Any updates maybe? or at least an example how to do this in SAM before it is implemented

@sanathkr
Copy link
Contributor

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 :-)

@mbailey
Copy link

mbailey commented Aug 17, 2017

Any update @sanathkr ?

We've implemented a swagger file to get around for the time being but are keen to remove it.

@dinvlad
Copy link

dinvlad commented Sep 5, 2017

@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?

@bslatner
Copy link

Any updates on this? As it happens, I need it desperately :)

@prafulmehrotra
Copy link

Any update on ETA for this feature?

@lskrajny
Copy link

Are there any updates?

@sowmitranalla
Copy link

Any updates on this? It would be such a wonderful feature to have.

@frehner
Copy link

frehner commented Nov 2, 2017

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

Source

@sowmitranalla
Copy link

Thanks for the update @frehner ! Appreciate it!

@awHamer
Copy link

awHamer commented Dec 4, 2017

any updates on this? Thanks!

@jackmusick
Copy link

Also looking for an update... Sounds like it was going to be just a couple of weeks back in July.

@ghost
Copy link

ghost commented Dec 12, 2017

+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.

@jarrettj
Copy link

+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.

@sdmolloy
Copy link

+1

@sanathkr
Copy link
Contributor

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

@ghost
Copy link

ghost commented Jan 3, 2018

My workaround for an implicit API

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.

CORS has two cases you must consider

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:

To cover CORS simple requests

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.

To cover pre-flighted requests

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.

Hope this helps

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.

@jfuss
Copy link
Contributor

jfuss commented Jan 8, 2018

We created a parent task to track this. Please see #248 for updates.

Closing as duplicate of #248

@rabowskyb
Copy link
Contributor

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.

@praetp
Copy link

praetp commented May 2, 2019

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..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests