1
votes

I have an API Gateway (of type HTTP) that integrates with a Lambda function. I am trying to call that function from localhost as follows:

const jwt = getAuthToken();
const formBody = new FormData();
formBody.set('user', 'test');

const res = await fetch('https://example.execute-api.eu-west-1.amazonaws.com/default/GetShareValue', {
method: 'POST',
body: formBody,
headers: {
   'Authorization': jwt
   }
});

However, I get this error message:

Access to fetch at 'https://example.execute-api.eu-west-1.amazonaws.com/default/GetShareValue' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

I understand that incorrect CORS settings will prevent the browser from displaying what the Lambda function returns. However, my API Gateway has the CORS settings specified:

enter image description here

According to this thread, setting the headers in the Lambda function's response is important. I have set them to be identical. Here's my Lambda function:

exports.handler = async (event, context) => {
    let body;
    let statusCode = '200';
    const headers = {
        'Content-Type': 'application/json',
        "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,X-Amz-Security-Token,Authorization,X-Api-Key,X-Requested-With,Accept,Access-Control-Allow-Methods,Access-Control-Allow-Origin,Access-Control-Allow-Headers",
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Methods": "*",
        "X-Requested-With": "*"
    };

try {
    body = "Success!!";
} catch (err) {
    statusCode = '400';
    body = err.message;
} finally {
    body = JSON.stringify(body);
}

return {
    statusCode,
    body,
    headers,
};

};

I can call this endpoint in Postman - which doesn't care about CORS - so I know it's working. I am starting to think that this is a bug within AWS itself.

2
Have you tried re-deploying the API?peter n
Yeah, I have created another identical API and it had the same issue.MSOACC
Alright, I ended up re-created the API Gateway as a REST API rather than an HTTP API. Even then I had CORS issues with the solution buried deep within AWS docs. This is such a bad platform from a user-experience point of view. Azure is a breeze in comparison.MSOACC
Do you mind sharing the solution? I agree CORS has been a quirky process with API Gateway.peter n
Hi Peter. I will post my solution as an answer to this question.MSOACC

2 Answers

3
votes

Answering my own question for the benefit of @peter n.

In the end I abandoned the HTTP API Gateway. I believe there is a bug in AWS as the exact same Lambda function worked when I triggered it via a REST API. Here's what I did.

  1. Create a new API Gateway
  2. Select the type of the API Gateway as REST API. I previously had selected HTTP. The REST API gives us more control; the HTTP API is simple (and 70% cheaper)
  3. On the Resources page, click Actions > Create Resource
  4. Create the resource; if you see any checkbox mentioning CORS then check it. I can't remember if this occurs at this stage.
  5. Then click Actions > Create method
  6. Select any method (like POST, GET etc.) other than "Any". This is important - if you select "Any" there are further issues with CORS. Why? Who knows, AWS is a user-unfriendly platform.
  7. Once you've configured the method and got it firing at your Lambda function (or whatever you're integrating with) test it with something like Postman, which doesn't care about CORS.
  8. Finally, to get CORS working click Actions > Enable CORS

I entered the following settings:

Methods: POST

Access-Control-Allow-Methods: OPTIONS, POST

Access-Control-Allow-Headers: 'Content-Type,X-Amz-Date,X-Amz-Security-Token,Authorization,X-Api-Key,X-Requested-With,Accept,Access-Control-Allow-Methods,Access-Control-Allow-Origin,Access-Control-Allow-Headers'

Access-Control-Allow-Origin: '*'

Then click "Enable CORS and replace existing CORS headers".

You're not done yet. This still won't solve the issue if you're targetting a Lambda function. You need to add the CORS headers to the actual response of your Lambda.

Here's the full code for my Lambda:

exports.handler = async (event, context) => {
   let body = JSON.stringify('Success!');
   let statusCode = '200';
   const headers = {
      'Content-Type': 'application/json',
      "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,X-Amz-Security-Token,Authorization,X-Api-Key,X-Requested-With,Accept,Access-Control-Allow-Methods,Access-Control-Allow-Origin,Access-Control-Allow-Headers",
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Methods": "*",
      "X-Requested-With": "*"
   };
    
   return {
      statusCode,
      body,
      headers,
   };
};

I am not sure if it matters that the CORS settings you return in the Lambda are the same CORS settings that you applied in the AWS Console.

This is a real headache and, unless I am doing something wrong, AWS have implemented this very badly.

Update: After having further issues with this I thought I'd update my answer to state that the request you make also seems to impact on CORS. I was making changes to my API and the CORS error started happening again; eventually, the only solution was to remove some of the headers in the request I was sending to AWS.

This caused CORS errors:

const res = await fetch('https://123.execute-api.eu-west-1.amazonaws.com/test', {
   method: 'POST',
   headers: {
      'Authorization': jwt,
      "Access-Control-Allow-Origin": "*" // This had to be removed to fix the CORS error
   }
});

As you can see, I was specifying Access-Control-Allow-Origin in the outbound request to my API. This was always there and never caused issues, but seemingly now I can comment and uncomment that line in the request and get/not-get the CORS error.

Update #2: A further update to save anyone who may be banging their head against the wall for this.. after adding new sources and methods to an existing API Gateway, be sure to click Actions > Deploy API so your changes are actually live.

AWS really need to clean up the whole UI for API Gateway and Lambda. It is not clear at all that you have unpublished changes or that publishing is even required; the UI makes it seem like any changes are instantly live.

1
votes

The main problem is in the answer provided by the Options method. For some reason, the mock response, at least in my case, does not work correctly, so I have linked it with a lambda whose sole purpose is to respond with a 200 code and returning all the necessary headers.

Therefore, what you have to do is, inside the options method of the resource with CORS enabled, change the integration request to Lambda.

Once this is done, we must develop the lambda so that it returns a 200 as a response and accepts all the necessary headers:

{
    'statusCode': 200,
    'headers': {
        'Content-Type': 'application/json',
          "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,X-Amz-Security-Token,Authorization,X-Api-Key,X-Requested-With,Accept,Access-Control-Allow-Methods,Access-Control-Allow-Origin,Access-Control-Allow-Headers",
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Methods": "*",
          "X-Requested-With": "*"
    },
    'body': json.dumps({})
}