CORS defeats AWS LAMBDA :( - amazon-web-services

I've been reading a lot on CORS/Lambda/AWS API Gateway configuration, including AWS's setup help:
http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html
And this great article:
https://serverless.com/framework/docs/providers/aws/events/apigateway/
However, CORS is still defeating my API Gateway / Lambda function and I can't figure it out. Would love some help.
The meat of it seems to be that API Gateway proxies the request onto Lambda and you need to set "Access-Control-Allow-Origin": "*" headers in your Lambda function in addition to enabling CORS in the API Gateway, which I've done. I am getting a 200 response and can see the header being sent back correctly from my Lambda function. However, Chrome/FF still give me a CORS error. Any idea why?
Here are my request headers:
Host: myawshost.execute-api.us-west-2.amazonaws.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: application/json, text/javascript
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Origin: null
DNT: 1
Connection: keep-alive
And the 200 response headers:
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 74
Content-Type: application/json
Date: Fri, 23 Dec 2016 08:35:02 GMT
...
That all looks nice and successful right? But yet, I get no JSON back from Lambda and this error message in console:
"Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://myawshost.execute-api.us-west-2.amazonaws.com/prod/view?id=272. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). (unknown)"
But Access-Control-Allow-Origin is not missing! I can see it there in the response.
Any thoughts?

I've also spent quite some time researching this, and what made it work for me was returning the following in the lambda handler (Python):
return {
'statusCode': status_code,
'headers': {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
'body': status_message if status_code == 200 else 'Failure'
}

Hello, i fixed that problem by setting cors and requested headers in
Api Gateway
first i set the headers i will want in my Method Request after
that i want to include them in the enable CORS menu
after that dont forget to deploy the api to public the new changes in
the api
I hope it will fix it
Regards

Related

CloudFront is removing Access-Control-* headers from my Origin

I have a CloudFront distribution with a custom origin to an APIGateway that forwards calls to a Lambda which is my API code. I have a separate CloudFront distribution for my static single-page website. My website is not working because it is getting CORS errors when calling my API on a separate subdomain. It is my Lambda that is currently responsible for sending back CORS headers.
Looking into it, it seems CloudFront is removing CORS headers from the responses from the APIGateway and I cannot figure out how to get it to allow the headers. I can make the same call directly to my APIGateway and I get the correct response headers.
Request:
OPTIONS https://api.mywebsite.com/some/endpoint
User-Agent: ...snip...
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
Referer: https://www.mywebsite.com/
Origin: https://www.mywebsite.com
Connection: keep-alive
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
APIGateway Response:
200 OK
Date: Fri, 27 Jan 2023 03:47:55 GMT
Content-Type: application/json
Content-Length: 0
Connection: keep-alive
x-amzn-RequestId: ...snip...
X-XSS-Protection: 1; mode=block
Access-Control-Allow-Origin: https://www.mywebsite.com
Access-Control-Allow-Headers: authorization
X-Frame-Options: DENY
x-amz-apigw-id: ...snip...
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Expires: 0
X-Content-Type-Options: nosniff
Access-Control-Allow-Methods: GET
Pragma: no-cache
Access-Control-Max-Age: 3600
CloudFront Response:
200 OK
Content-Type: application/json
Content-Length: 0
Connection: keep-alive
Date: Fri, 27 Jan 2023 03:51:58 GMT
x-amzn-RequestId: ...snip...
X-XSS-Protection: 1; mode=block
Accept-Patch:
Access-Control-Allow-Origin: https://www.cicerone.development.loesoft.com
Allow: GET,HEAD,OPTIONS
X-Frame-Options: DENY
x-amz-apigw-id: ...snip...
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Expires: 0
X-Content-Type-Options: nosniff
Pragma: no-cache
X-Cache: Miss from cloudfront
Via: 1.1 ...snip....cloudfront.net (CloudFront)
X-Amz-Cf-Pop: DFW56-P2
X-Amz-Cf-Id: ...snip...
The browser is rejecting the desired GET call because the PreFlight call didn't have the expected information. I suspect this is because of the missing one or more of the Access-Control-* headers.
I've tried configuring CloudFront a few different ways with no success. Original configuration, the default (only) behavior had a Cache policy and no assigned Origin Request policy or Response Headers policy. I tried adding the managed "All Viewer" managed Origin Request policy which should be sending all inbound request headers to my APIGateway. I did this just in case any headers were being removed in this case. This made no difference. I then added a Response Headers policy that set generic values for the various CORS headers and made sure the "override origin" flag was off so that the "Access-Control-*" headers coming from my origin would be used. This also did not solve the issue. I've tried various different configurations for all the policies but I'm not having much luck.
Additionally, if I have my UI bypass CloudFront and access my API directly, the API calls from the browser work w/o issue.
Is there a way to configure CloudFront to solve my CORS issue or even just to not filter any headers coming from the my origin?
Thank you in advance.
The issue turned out to be 2 parted. First, without an assigned Origin Request policy, CF was stripping many of the CORS headers before sending the request to the origin. This was causing the appropriate CORS response headers to not be generated by my backend Lambda. Next, adding the AllViewer Origin Request policy resulted in all responses returning 403 but never actually getting to my backend Lambda. It appears that setting this will cause the Host header to be sent with the down stream request, and APIGateway was rejecting the call.
I ended up creating my own Origin Request policy that included all the viewer headers except the Host header and then my downstream Lambda started getting the headers and returning the correct response headers that were then being echoed back by CF.
I did not need a Response Headers policy in place for this to work.

AWS GO SDK returns "AuthFailure: AWS was not able to validate the provided access credentials"

I am developing a simple automation tool using a Go Fiber HTTP server to start and stop AWS instances using the Go SDK v1.44.156.
The service listens to an endpoint at /csm/aws/:region/:instance_id/powerOn.
My code works well when I send requests from Postman. When I send requests using the Go HTTP client, AWS returns the following error:
AuthFailure: AWS was not able to validate the provided access credentials
The Postman request that works fine:
2022/12/23 16:26:12 Request came :#0000000100000003 - 127.0.0.1:7000 <-> 127.0.0.1:34976 - POST http://127.0.0.1:7000/csm/aws/us-east-1/i-0f9c5fe6b5c7b0a87/powerOn
Params: map[instance_id:i-0f9c5fe6b5c7b0a87 region:us-east-1]
Request: POST /csm/aws/us-east-1/i-0f9c5fe6b5c7b0a87/powerOn HTTP/1.1
User-Agent: PostmanRuntime/7.30.0
Host: 127.0.0.1:7000
Content-Type: application/json
Content-Length: 136
Accept: */*
Postman-Token: e27b899f-5125-497a-b154-61cd3214cd74
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
{"aws_access_key_id":"my-id","aws_secret_access_key":"my-key","account":"","region":""}
The Go request which returns the error:
2022/12/23 16:22:02 Request came :#0000000200000002 - 127.0.0.1:7000 <-> 127.0.0.1:34278 - POST http://127.0.0.1:7000/csm/aws/us-east-1/i-0f9c5fe6b5c7b0a87/powerOn
Params: map[instance_id:i-0f9c5fe6b5c7b0a87 region:us-east-1]
Request: POST /csm/aws/us-east-1/i-0f9c5fe6b5c7b0a87/powerOn HTTP/1.1
User-Agent: Go-http-client/1.1
Host: 127.0.0.1:7000
Content-Type: application/json
Content-Length: 136
Accept-Encoding: gzip
{"aws_access_key_id":"my-id","aws_secret_access_key":"my-key","account":"","region":""}
I searched on the web and I found information about this error message. It seems like it can be due to the time of the PC so I set my computer's time to automatic, but I see the same error.
My code was working a few days ago.

rejected by cors when using withCredentials, despiete access-control-allow-credentials and origin are set

I'm using AWS lambdas and cloudfront to serve a SPA.
Now that my lambdas are setting a cookie, I want to include that cookie in the requests I made to the backend (the cookie is HttpOnly and Secure).
Using Axios I set the withCredentials option to true and all my request are now being rejected because CORS.
The web app is being served from the main domain, while the backend lambdas are on the usual lambda weird UUID url. The lambdas are returning the proper headers, as you can see in the screenshot: access-control-allow-origin is set to the domain the web-app is being served from and access-control-allow-credentials is true. The screenshot is from the app without the withCredentials option activated, so it is being triggered from the web-app 100% sure.
Everything is being served over https with a valid certificate (I want to test this also on localhost, but that is a different story)
This is the error I'm getting on the console. One weird thing is that it claims that Access-Control-Allow-Credentials is set to '', which is not true
Access to XMLHttpRequest at 'https://p3doiszvgg.execute-api.eu-central-1.amazonaws.com/dev/sessions'
from origin 'https://pento.danielo.es' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check:
The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
Is there anything missing?
EDIT:
This are the headers that I'm sending. The problem with this headers is that they are obtained without the withCredentials flag, because if I add such flag the only headers I can see are the provisional headers.
:authority: p3doiszvgg.execute-api.eu-central-1.amazonaws.com
:method: POST
:path: /dev/sessions
:scheme: https
accept: application/json, text/plain, */*
accept-encoding: gzip, deflate, br
accept-language: en-GB,en;q=0.9,es-ES;q=0.8,es;q=0.7,en-US;q=0.6
authorization: Bearer the.bearer.token
cache-control: no-cache
content-length: 58
content-type: application/json;charset=UTF-8
origin: https://pento.danielo.es
pragma: no-cache
referer: https://pento.danielo.es/
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36
Here is a provisional headers screenshot:
The cookie sent by the server looks something like this:
Set-Cookie: refresh_token=uuid-string-with-letters-numbers; HttpOnly; Secure;
Finally I found the problem and a temporary solution (I'm not very happy with it).
The problem was not my lambda response, that was correct and including the required headers, the problem was with the preflight request. Your browser will send a preflight request almost for every CORS request you made, and, while that request was being successful it was missing some headers. This can be very confusing because the request that it is failing is your actual request (that is what the browser flags as failed) but the problem is on the preflight response.
To be fair, the error on the console was already pointing this out:
Response to preflight request doesn't pass access control check
But it is abit buried, easy to miss and the documentation about it is sparse.
The way I fixed it is by adding some extra props to the CORS definition of my serverless template:
authEcho:
handler: src/users/me.handler
events:
- http:
path: me
method: get
cors:
origin: https://frontend.domain.es
allowCredentials: true # <-- this is the key part
It is not clear on the serverless documentation, but those will be merged with the final response, so you don't need to specify everything or all the headers. The only thing I don't like is that I have to hardcode the origin, while on the actual labmda responses I can calculate it dynamically.

How to enable CORS in AWS API Gateway

I'm trying to enable CORS with Amazon's API gateway and Lambda, but I keep getting the error.
Access to XMLHttpRequest at 'https://<url>' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I've followed https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html and added this to lambda expression:
statusCode : statusCode,
body: responseBody,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
}
I've also gone to the API gateway and clicked the enable CORS button. If I look at the OPTIONS in the API gateway it says the
Access-Control-Allow-Headers
is present
If I run a test on the API gateway I can see the headers returned by the lambda expression and the expected headers are all there, but I'm still getting the same error message.
edit:
Logwatch isn't printing anything so I'm assuming that it's not even making it to my lambda function.
I can hit the API successfully from POSTMAN and it has the CORS headers
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Methods: OPTIONS,POST,GET
Here are the response headers I'm getting when I try in chrome.
content-length: 35
content-type: application/json
date: Sun, 03 May 2020 01:51:15 GMT
status: 400
x-amz-apigw-id: L7p_IEbuoAMFeaA=
x-amzn-errortype: BadRequestException
x-amzn-requestid: 7ed06b7d-951f-4774-9bfa-62f307ee5974
The problem is that I was POSTing the data incorrectly from my UI so it was failing model validation. After I disabled body validation it started working properly.
See this question:
AWS API Gateway - CORS + POST not working

CORS impossible on AWS Lambda HTTP API Gateway Integration

An AWS Lamba function (NodeJS) returning 3 HTTP headers: aaa, Access-Control-Allow-Origin and bbb was created:
exports.handler = async (event) => {
const response = {
statusCode: 200,
headers: { "aaa":"aaa", "Access-Control-Allow-Origin":"*", "bbb":"bbb" },
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
The function is integrated into a HTTP API (not REST API). In the HTTP API Gateway Configuration, Section "Configure CORS", the HTTP header "Access-Control-Allow-Origin" was set to "*". Please see the screenshot:
Gateway Config
The command "curl -i https://xxxxxxxxxx.execute-api.eu-central-1.amazonaws.com" proves that the HTTP Header Access-Control-Allow-Origin is explicitly removed, because only HTTP headers aaa and bbb are returned:
HTTP/2 200
date: Tue, 14 Apr 2020 11:01:58 GMT
content-type: text/plain; charset=utf-8
content-length: 20
aaa: aaa
bbb: bbb
apigw-requestid: K-S2EjVWliAEJKw=
Why on earth is this header still not present, even after "Configure CORS" was done?
(I'm googling now for more than two days in order to find a solution and it makes me go nuts)
As per Configuring CORS for an HTTP API -
If you configure CORS for an API, API Gateway ignores CORS headers
returned from your backend integration.
That's why the CORS headers from your Lambda (integration) are being ignored. This is one of the differences between the new HTTP APIs from the original REST APIs. In case of these APIs -
For a CORS request, API Gateway adds the configured CORS headers to
the response from an integration.
When you do a simple curl, that is not actual doing a cross-origin request. Hence, you don't see the CORS headers that would be set by the HTTP API. To verify if a CORS request works, I passed an Origin header in the below request and I can see the CORS headers along with my custom headers from Lambda -
$ curl -v -X GET https://$API_ID.execute-api.$AWS_REGION.amazonaws.com -H "Origin: https://www.example.com"
< HTTP/2 200
< date: Tue, 14 Apr 2020 18:02:26 GMT
< content-type: text/plain; charset=utf-8
< content-length: 18
< aaa: aaa
< bbb: bbb
< access-control-allow-origin: https://www.example.com
< access-control-expose-headers: date, x-api-id
Below is a snippet of my CORS configuration on the API. I added Access-Control-Allow-Origin value as https://www.example.com and passed this as a part of the Origin header in my curl request. Such a request would qualify as CORS.
For POST/PUT requests, you'll need to white list the content-type header.
Putting the wildcard doesn't do the trick for some reason, you need to explicitly whitelist it.
For Googlers:
If your OPTIONS preflights succeed but no Access-Control- headers present, and if you are testing using CURL, take extra attention to spell required headers for a preflight:
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
Origin: https://theaws.blog
If wrongly spelt, OPTIONS succeeds with 204 No Content, but no Access-Control- headers thus render the preflight result invalid.
Also check you have enough scope for the parameters, as long as your requested method and headers are a subset of the parameter, you will get Access-Control- headers. If not, you will get 204 No Content instead, which I would say it's not very informative!