CORS not working with Lambda Proxy with container runtime - amazon-web-services

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.

Related

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.

Api Gateway with Lambda proxy integration via serverless not decoding response

I've set up an API Gateway integration with a a lambda proxy integration, done automatically with the serverless framework. I usually have no problem with JSON returns, but doing this with http4k and kotlin, all my responses come back still base64 encoded.
Here are my logs for a response (from a mock inventory server)
f874ff6f-291c-4f9e-b106-f5f4a26e25e5) Endpoint response body before transformations:
{ "statusCode": 200, "headers": { "Content-Type": "application/json; charset=utf-8" }, "body": "W3siaXRlbSI6eyJpdGVtSWQiOiJjOTNjM2MxYy1iMWFjLTRiMTUtYjI1Yi1lMWVmNWY2ZDYxNzIiLCJuYW1lIjoidGVzdCIsInN0b2NrIjoxLCJzdGF0dXMiOiJBVkFJTEFCTEUifSwicHJpY2UiOnsiaXRlbUlkIjoiYzkzYzNjMWMtYjFhYy00YjE1LWIyNWItZTFlZjVmNmQ2MTcyIiwiYW1vdW50IjoxMDAwLCJjdXJyZW5jeSI6ImpweSJ9fV0=", "isBase64Encoded": true }
And then
(f874ff6f-291c-4f9e-b106-f5f4a26e25e5) Method response body after transformations: W3siaXRlbSI6eyJpdGVtSWQiOiJjOTNjM2MxYy1iMWFjLTRiMTUtYjI1Yi1lMWVmNWY2ZDYxNzIiLCJuYW1lIjoidGVzdCIsInN0b2NrIjoxLCJzdGF0dXMiOiJBVkFJTEFCTEUifSwicHJpY2UiOnsiaXRlbUlkIjoiYzkzYzNjMWMtYjFhYy00YjE1LWIyNWItZTFlZjVmNmQ2MTcyIiwiYW1vdW50IjoxMDAwLCJjdXJyZW5jeSI6ImpweSJ9fV0=
I've try to set binary media types to /, but still no luck.
Has anyone seen anything like this before?

Call to API Gateway returning CORS error despite headers being set

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

CORS access error for PUT method, api on aws hosted on elastic beanstalk

I've deployed an api on AWS API Gateway using http custom integration
I've enabled CORS as seen below:
(source: upload.cat)
For both GET and PUT methods, I am getting the following error:
"url from origin [my origin] has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource"
Code:
fetch(url, {
method: 'POST',
mode: "cors",
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token' //copied from screenshot above
},
body: JSON.stringify(data)
})
.then(response => console.log('Success:', response))
.catch(error => console.error('Error:', error));
}
Access-Control-Allow-Headers are set in the server, you should remove it from your code. It won't make any difference if you put it in your request.
Is your API Gateway method protected by Lambda Authorizer or configured to use an API Key? If so, request might be rejected by one of them and response won't include Access-Control-Allow-Origin header so browser's preflight check will be failed.
You also need to check what response is being returned by API method integration. In case of Proxy Lambda, response should include Access-Control-Allow-Origin header.
Take a look at integration response documentation.

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