How to set up CORS for an AWS Lambda Proxy REST API resource

Let’s see how you set up CORS using a Lambda Proxy API and AWS SAM. Heads up! This guide will also help fix a CORS error that fails in the browser but works in CURL.

About CORS

Cross-Origin Resource Sharing (CORS) is a way to tell browsers which domain, HTTP methods, and HTTP header(s) to trust. Therefore, this mechanism allows a server to indicate any origins (domain, scheme, or port) (and more) other than its own from which a browser should permit loading resources.

This header-based mechanism relies on pre-flight requests made by clients before actually sending the intended request off. The pre-flight request sends a request to the OPTIONS method of the API to achieve this.

When do you need CORS?

You can group CORS requests into two types: simple requests and non-simple requests.

A CORS request is simple if all the following are true:

  • The API Only accepts GET, HEAD, and POST requests.
  • The request includes the Origin header for the POST method request.
  • The request payload content type is: text/plain, multipart/form-data, or application/x-www-form-urlencoded.
  • The API resource does not contain custom headers.
  • And lastly, any additional requirements are listed in the Mozilla CORS documentation.

All other CORS requests are non-simple requests and require your API to enable CORS support.

About AWS SAM

The Serverless Application Model (SAM) is a framework where you can build serverless applications. It provides shorthand syntax for defining functions, APIs, and more in one document using YAML – without having any setup or maintenance hassle!

Let’s Get Started!

First, you need to set up the CORS config in the globals section of your AWS SAM template.

Consequently, your globals section should look like this:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: CORS via Globals

Globals:
  Api:
    Cors:
      AllowOrigin: "'*'"
      AllowHeaders: "*"
      AllowMethods: "'*'"

Resources:
  MyFunction:
...

Next, you need the CORS config to REST API resource.

This setting means your API section should look like this:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: CORS via resource

Resources:
  MyRestApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      Cors:
        AllowOrigin: "'*'"
        AllowHeaders: "'*'"
        AllowMethods: "'*'"
...

Finally, you must add the CORS config to the REST API response.

Therefore, your Lambda handler should respond with something like this:

{
        "statusCode": status_code,
        "headers": {
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Headers": "*",
            "Access-Control-Allow-Methods": "*",
        },
        "body": json_str,
    }

Beware of The ApiKeyRequired Parameter!

In some cases, the ApiKeyRequired can cause problems for JavaScript frameworks. For example, setting this parameter in the “AWS::Serverless::Api” section enables the “x-api-key” header for preflight checks. However, the JavaScript framework may not always include the “x-api-key” in the pre-flight checks.

The example below sets the ApiKeyRequired parameter in the API Resource block:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: CORS via resource

Resources:
  MyRestApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      Auth:
        ApiKeyRequired: true   # sets for all methods
      Cors:
        AllowOrigin: "'*'"
        AllowHeaders: "'*'"
        AllowMethods: "'*'"
...

Example output in API Gateway:

You can avoid this error by setting ApiKeyRequired to false and then applying this setting manually on the “AWS::Serverless::Function” resource.

Consequently, your Function resource should look like this:

  ApiPostCustomer:
    Type: "AWS::Serverless::Function"
    Properties:
...
      Events:
        apiPostCustomerEvent:
          Type: "Api"
          Properties:
...
            Auth:
              ApiKeyRequired: true
...

As a result, your API Gateway will look like this:

Best Practice Considerations

The examples mentioned above set the access-control-allow-origin, access-control-allow-headers, and access-control-allow-methods to “*” or any. However, it would be best never to allow “any” as this is a terrible security practice. Thus, you need to be more specific and avoid “any”.

Be careful when using * as this could allow the browser access to unintended resources.

How can you test the CORS configuration?

CURL is a great tool to test your CORS configuration:

curl --verbose --location --request <method> '<url>' 

But, of course, you must change the method and the URL parameters accordingly.

For example, here you need to:

  • Set the method to “OPTIONS” or the intended HTTP method like “GET”.
  • URL is the URL of your REST API

You will, as a result, see an output like this:

> curl --verbose --location --request OPTIONS 'https://czr4jjjj.execute-api.us-east-1.amazonaws.com/v1/customer' 
*   Trying 3.236.158.2:443...
* TCP_NODELAY set
...
...
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200 
< date: Tue, 10 May 2022 01:01:31 GMT
< content-type: application/json
< content-length: 3
< x-amzn-requestid: 4c3aaa9d-5b98-48ad-8c98-d7202c0fce97
< access-control-allow-origin: *
< access-control-allow-headers: *
< x-amz-apigw-id: R4noxGd_oAMFoVA=
< access-control-allow-methods: *
< x-amzn-trace-id: Root=1-6279b96b-4df2413134950b6753c093f4
< 
{}
* Connection #0 to host czr4jjjj.execute-api.us-east-1.amazonaws.com left intact

If the ApiKeyRequired parameter were set on the API resource, you would see a forbidden response:

...
< HTTP/2 403 
< date: Mon, 09 May 2022 22:37:59 GMT
< content-type: application/json
< content-length: 23
< x-amzn-requestid: 24b15573-feb7-4c8f-b4d2-00d6ef7129c9
< x-amzn-errortype: ForbiddenException
< x-amz-apigw-id: R4SnJHlMoAMFwgw=
< x-amzn-trace-id: Root=1-627997c7-1dc99815469066d339ab5b67
< 
* Connection #0 to host czr4jjss0e.execute-api.us-east-1.amazonaws.com left intact
{"message":"Forbidden"}

Wrapping Up

You have learned how to enable CORS when using AWS Lambda Proxy API and AWS SAM. In addition, you have learned how to enable the API Key required parameter for all methods except OPTIONS, which can cause a “forbidden” error. Therefore, you should now be able to access your API resources by CURL and the browser.

You May Also Be Interested In

Sources:

About Anto Online

Anto's journey into the tech world began in 1999 as a Desktop Support Engineer. His passion for technology soon led him down the path of software development, where he honed his skills for several years before transitioning into the world of consulting. Today, Anto serves as an enterprise application consultant for a leading SaaS software provider, specializing in the realm of AWS's serverless technologies. By day, Anto guides customers on their journey to harness the power of serverless technologies, helping them unlock new levels of efficiency and innovation. But as the sun sets, Anto's passion for cloud computing takes center stage. He dives into the depths of Python, exploring cutting-edge concepts and experimenting with technologies that lie beyond the boundaries of his daily work. Recognizing that even the most dedicated tech enthusiasts need a helping hand, Anto collaborates with a team of fellow cloud aficionados. Each member brings their unique expertise to the table, and Anto takes immense pride in fostering their growth and learning. Together, they form a formidable force, pushing the boundaries of cloud computing and exploring the limitless possibilities that lie ahead. Through his unwavering commitment to learning, experimentation, and collaboration, Anto embodies the spirit of a true technologist. His enthusiasm for cloud computing is infectious, inspiring others to venture into this ever-evolving domain and discover its transformative potential.

View all posts by Anto Online

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.