Call to API Gateway returning CORS error despite headers being set - amazon-web-services

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

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.
Create a new API Gateway
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)
On the Resources page, click Actions > Create Resource
Create the resource; if you see any checkbox mentioning CORS then check it. I can't remember if this occurs at this stage.
Then click Actions > Create method
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.
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.
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.

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({})
}

Related

CORS not working with Lambda Proxy with container runtime

I'm trying to CORS to my lambda running in a docker image. I've added the headers to my lambda response like so
return {
"statusCode": 200,
"headers": {
"Content-Type": "application/json",
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST",
},
"body": "testing"
}
And enabled the Lambda Proxy Integration in API Gateway. When I hit the endpoint in PostMan, everything is returned exactly as expected for headers as you can see below.
However, when using with my application in chrome, I get the 403 forbidden and that Access-Control-Allow-Origin is not present when seemingly it is. I've looked at many similar issues to this but none seem to resolve mine
Are you handling the pre flight request to the OPTIONS resource? You need to make sure the OPTIONS resource has the following response:
{
"Access-Control-Allow-Headers": ["Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token"],
"Access-Control-Allow-Methods": ["OPTIONS,POST"],
"Access-Control-Allow-Origin": ["*"],
"Content-Type":["application/json"]
}
I would configure the OPTIONS endpoint as a mock response in API Gateway so that it always returns the above. Then requests to your POST endpoint should work as expected from your browser.
Make sure you don't place any auth requirements on the OPTIONS resource.

Spring Cloud Function AWS Lambda with proxy integration on APIGW : CORS issue

We have created our AWS Lambda function using Spring Cloud function. This function returns APIGatewayProxyResponseEvent response. Sample below
{
"statusCode": 200,
"headers": {
"Access-Control-Expose-Headers": "Access-Control-Allow-Methods,Access-Control-Allow-Origin",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET",
"Access-Control-Max-Age": "200",
"Access-Control-Allow-Headers": "Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers",
"Content-Type": "application/json"
},
"multiValueHeaders": null,
"body": "response Data json Body",
"isBase64Encoded": false
}
APIGW uses Lambda proxy integration , and hence there is no option for response mappings.
We have enabled CORS by using Actions on the console. This automatically adds the OPTIONS method where we have configured the 200 response with below headers
Access-Control-Max-Age : '200'
Access-Control-Allow-Headers : 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'
Access-Control-Expose-Headers : 'Access-Control-Allow-Methods,Access-Control-Allow-Origin'
Access-Control-Allow-Origin : '*'
Access-Control-Allow-Methods : 'GET,OPTIONS'
The above steps are in sync with the AWS documentation AWS - How to CORS Lambda proxy
We deployed the API in a stage and are able to access it via Postman. On accessing from our web-application, which is currently on localhost we get CORS error.
In Network tab its visible that the preflight request (OPTIONS) returns 200 OK and the required CORS headers. However the actual GET call still fails , saying "CORS Error".
The issue is that APIGW is not copying the headers returned in the APIGatewayProxyResponseEvent object to final APIGW Response headers
Is this a known issue or am I missing something
Edit
Screen shot of APIGW lambda proxy
Screen shot from APIGW response (Testing from console)
Network tab in browser developer options showing preflight request successful
Edit2
Adding Console output
Edit 1
On checking the spring cloud milestone release, This issue has been addressed starting 3.2.0-M1 . (Currently available release is 3.1.5). Once this is released my previous approach of sending APIGatewayProxyResponseEvent as output will work just fine.
#Oleg Zhurakousky can confirm
Original Answer below : (almost a work around)
Got help from AWS support and understood that the response being returned from the Spring Cloud function was being modified. This in turn resulted in all the headers being encapsulated as part of Lambda response body.
My previous implementation of the function was
#Bean
public Function<APIGatewayProxyRequestEvent,APIGatewayProxyResponseEvent> testFunc2(){
return event -> {
System.out.println(event);
APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();
Map<String,String> hdr = new HashMap<>();
hdr.put("Access-Control-Allow-Origin","*");
response.setHeaders(hdr);
response.setStatusCode(HttpStatus.OK.value());
response.setBody("Hello World!");
return response;
};
}
I had to change it to below to make sure the headers are treated as http headers and not a part of the lambda response body
#Bean
public Function<APIGatewayProxyRequestEvent, Message<APIGatewayProxyResponseEvent>> testFunc3(){
return event -> {
System.out.println(event);
APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();
Map<String,Object> hdr = new HashMap<>();
hdr.put("Access-Control-Allow-Origin","*");
response.setStatusCode(HttpStatus.OK.value());
response.setBody("Hello World!");
Message<APIGatewayProxyResponseEvent> finalResponse = new GenericMessage<APIGatewayProxyResponseEvent>(response,hdr);
System.out.println("Response prepared " +response);
System.out.println("Final Response being returned " +finalResponse);
return finalResponse;
};
}
The actual entry point into Spring cloud function is org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest
Here while preparing response spring takes the returned value from the function as Message (org.springframework.messaging) Payload.
Hence In order to set http headers we need to return a Message<APIGatewayProxyResponseEvent> instead of APIGatewayProxyResponseEvent. Here we explicitly set our http headers in Message headers.

Access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response

I have prepared an Lambda function using Express(node.js) and enabled the Authorization with IAM as well.
The API is working in the Postman as per below link :
https://www.youtube.com/watch?v=KXyATZctkmQ&t=35s
As I'm fairly new with CORS policy and API concepts. I'm trying to access the sample using Ajax call.
So far I have prepared the Authorization Header as per documentation and few reference.
Git Repo Link : https://github.com/mudass1r/aws-iam-authorization.git
Reference Link for Signature Generation :
https://gist.github.com/davidkelley/c1274cffdc0d9d782d7e
I have Enabled the CORS from AWS API Gateway for my API as well.
PS : The API is Deployed using Serverless Framework.
Step 1 : The error I'm facing initial when I dont include any headers:
Step 2 : Later when I add headers:
$.ajax(Signer(credentials, {
url: <AWS API URL>,
type: 'GET',
dataType: 'json',
async: true,
crossDomain: true,
contentType: 'application/json',
headers: {
"Access-Control-Allow-Origin" : "*"
},
success: function(data) {
console.log(data);
}
}));
After which I get the following error:
In my previous experience with this error we only need to enable the CORS for the API which resolves this issue. But the same is not in this cases. Following is the structure of the API resource.
I have been stuck in this problem for few day and came across some CORS policy reference links as well.
https://fetch.spec.whatwg.org/#http-cors-protocol
Thanks everyone for your inputs and help.
Answer :
First of all the problem was not in header content. It was in the Authorization String which I was generating for AWS API Gateway authorization.
As pointed by #sideshowbarker. We don't need to add any headers in the ajax call.
The response header are enough to handle the API call.
app.use(cors())
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method,X-Access-Token,XKey,Authorization');
next();
});
Also I switched to Firefox for debugging the API call, where I found following errors.
Because I switched to Firefox, I could see the response from AWS from which I could further debug and fix the issue.
Issue in the CanonicalRequest function :
Before
'content-type;host;x-amz-date;'
After
'content-type;host;x-amz-date'
Thanks everyone for your inputs and help.
I have updated the git repo. Please refer the same.
https://github.com/mudass1r/aws-iam-authorization

Can I setup a custom response header to all my lambda functions?

I'm using lambda with CORS, and I currently have to setup manually for every response in every lambda function the following header:
const response = {
headers: {
"Access-Control-Allow-Origin" : "*", // Required for CORS support to work
"Access-Control-Allow-Credentials" : true // Required for cookies, authorization headers with HTTPS
},
statusCode: 200,
body: JSON.stringify(params.Item),
};
Is there a way to setup the headers as a default response for all my lambdas? Perhaps in API Gateway or somewhere else so I don't have to manually add it for every single response?
You should set up CORS handing at the API gateway instead. That way you don't have to pay for your lambda to handle CORS OPTION requests.
https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html
API gateway can help achieve it, especially when we have mostly static response headers to add.
(1) go to resources > 'method response'... add your response headers for the 200 (OK) HTTP status (first image)
(2) go to resources > 'integration response'... add mappings to your values (second image)
for dynamic data in the response headers, lambda works best

How to add CORS header to AWS API Gateway response with lambda proxy integration activate

I use lambda as backend for AWS API Gateway with lambda proxy integration and want to add CORS into response header.
According to documentation:
http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html
However, you must rely on the back end to return the Access-Control-Allow-Origin headers because the integration response is disabled for the proxy integration.
How can I program it in my lambda function with Python.
To create OPTIONS method you can enable it from the Gateway
Navigate to your Gateaway, Select Resources from left side
Select endpoint, on top there will a button "Action", there you will need to select "Enable CORS", save the settings.
Deploy the Gateway.
It will create a method OPTIONS on the resource(endpoint)
for GET/POST other HTTP Verbs you will need to manage it from your code, in case of python
return {
'statusCode': "200",
'body': json.dumps({"test" : "123"}),
'headers': {
"Content-Type" : "application/json",
"Access-Control-Allow-Origin" : "*",
"Allow" : "GET, OPTIONS, POST",
"Access-Control-Allow-Methods" : "GET, OPTIONS, POST",
"Access-Control-Allow-Headers" : "*"
}
}
for other unhandled cases like IntegrationTimeout (504) or Error in your code (502), you can configure default response headers at API Gateway Level. refer Default Response Headers: AWS API Gateway w/ Proxy Integration
you need to add a method "options" to your api gateway and using a proxy lambda... return
result.headers = { "Access-Control-Allow-Origin": "domain.com" }
so when the browser will first call options to your server it will return the CORS headers.
the thing is that, by default your lambda method will be called for "any" method, so you need to change the default one to get,post or whatever you need
note: you could also use the same method, like any or options,get,post and if it is a options call, only return status 200 and the cors header. it depends if you are using or not an auth method for get,post,etc
there is an option in Lambda console "Enable CORS" if you are just using lambda with nothing strange