API Gateway HTTP API CORS - amazon-web-services

I am using the new API Gateway HTTP which during the configuration enables you to add CORS. So I have set the Access-Control-Allow-Origin Header with the setting *.
However when I make a request using Postman I do not see that header and this i causing my VueJS Axios request to fail.
I previously used a Lambda Proxy Integration and did the following in my Lambda
"headers": {
"Access-Control-Allow-Origin": "*"
}
However the new HTTP API just does not seem to implement CORS. Maybe I am missing something simple.
--EDITS--
So I have continue to find an answer and came across a blog post from the guys at Serverless who set the following
It’ll ensure following headers:
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers:
Content-Type, X-Amz-Date, Authorization, X-Api-Key, X-Amz-Security-Token, X-Amz-User-Agent
Access-Control-Allow-Methods:
OPTIONS, and all the methods defined in your routes (GET, POST, etc.)
I have tried these and redeployed and still only get the standard headers
Thanks

For anyone using HTTP API and the proxy route ANY /{proxy+}
You will need to explicitly define your route methods in order for CORS to work.
Wish this was more explicit in the AWS Docs for Configuring CORS for an HTTP API
Was on a 2 hour call with AWS Support and they looped in one of their senior HTTP API developers, who made this recommendation.
Hopefully this post can save some people some time and effort.

If you have a JWT authorizer and your route accepts ANY requests, your authorizer will reject the OPTIONS request as it doesn't contain an Authorization/Bearer token. To resolve this issue, you need to explicitly point your route to the HTTP request/method you need. E.g. POST
In that case, your authorizer will ignore the OPTIONS request without a JWT and proceed with the required request.

(This is answer just for AWS HTTP api gateway/AWS api gateway v2).
I have met the same problem and I asked the AWS support finally. The tricky thing is actually CORS had been already working after we configured it, but I just didn't get how to test it (it's different from AWS REST API ), when test we need to specify the Origin(should be same with your setting:Access-Control-Allow-Origin in cors, like: http://example.com) and the Request method.
For example:
curl -v -X GET https://www.xxx.yy/foo/bar/ -H 'Origin:http://example.com' -H 'Access-Control-Request-Method: GET'
Then it would return the CORS header you had set in response header:
access-control-allow-origin, access-control-allow-methods, access-control-allow-headers, etc.
I just hope this can save time for who met the similar problem.
By the way, I hope AWS can update this test way to offical Document(it's not very useful, but most of people must be saw it before find real answer):
https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-cors.html

My issue was that I was sending this headers :
Access-Control-Allow-Origin
Access-Control-Allow-Methods
Access-Control-Allow-Credentials
From the web request and it was messing with the Access-Control-Allow-Headers. I removed them and it's fine now.
To debug CORS issue with AWS HTTP API, try to put a * in each field of the CORS configuration in the AWS Console and reconfigure each field one by one.

So even though i was getting cors headers in my post call, browsers were still failing,,
My solution was
explicitly create an OPTIONS route with same path {proxy+}
attach same lambda integration to it
have the lambda function return early with success headers
if (method === 'OPTIONS') {
return {
headers: { 'Access-Control-Allow-Origin': '*' },
statusCode: 200
}
}

tldr; x-api-key
It took some searching and trial and error, but I got CORS working for API Gateway HTTP API. For the very basic GET request from a jQuery ajax call here is what I had to do:
AWS Console CORS settings, needed Access-Control-Allow-Headers: x-api-key
Then, in my ajax call, I had to send a dummy x-api-key (I did not configure one, so not sure why it wants it.):
$.ajax ({
url: 'https://xxxxxxx.execute-api.us-east-1.amazonaws.com/prod/stuff/',
crossDomain: true,
method: 'GET',
headers: {
"accept": "application/json",
"X-Api-Key":"blablablalla"
},
success:function(data){
doStuff(data);
},
error: function(){
alert('error');
}
})
You may need additional configuration depending on your situation, but the x-api-key was what I narrowed down as the oddest undocumented requirement.

Related

Cannot query AWS API Gateway using API Key and CORs

I'm almost complete with a new web app, however, I'm getting a 403 error from AWS's API Gateway due to a CORs issue.
I'm creating a Vue app and using Vue's axios library. CORs is enabled and the request works with API Key Required option turned off in AWS's API Gateway by sending the following:
axios
.get('My-URL-Endpoint',{headers: {})
.then(response => (this.passports = response.data ));
When I turn on API Key Required functionality inside AWS's API Gateway. It works when I use Postman along with including x-api-key: My-API-Key. However, using Vue's axios it does not work and returns error 403:
axios
.get('My-URL-Endpoint', {headers: {
'x-api-key': 'My-API-Key'
}})
.then(response => (this.passports = response.data ));
My first instinct is that the problem is related to how Axios is sending the request through the browser. From what I can gather it looks like the pre-flight check is failing because I am receiving the following error within the browser:
Access to XMLHttpRequest at 'My-URL' from origin 'http://localhost:8080' 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.
Sure enough it looks like there is no access-control-allow-origin key in the response. So I added access-control-allow-origin to the response of the 403 message and got a new error "It does not have HTTP ok status"
I've been trying nearly everything to get this to work! I came across stackoverflow answer where it seems like the person was suggesting that API Key Required Key functionality can't work with CORs. This kind of seemed like that cannot be true. It would be a pretty crippling restriction.
So my question is how to get the browser's pre-flight check to work along with CORs and API Key capability inside AWS's API Gateway?
Thank you!
If you have enabled cors on your api gateway, the next place to look is the application code such as lambda. Make sure the Lambda is returning the correct cross origin headers in both successful and failure scenarios.
First of all you can check if the request is reaching the lambda from the cloud watch logs. Another way to check this is to temporarily point the Api gateway target to the Mock end point.
If the mock endpoint works, then the problem is the application code. otherwise the problem is in your api gateway end point.
Also note that any headers you use should be white listed in the Access-Control-Allow-headers section when you enable to cors for your method/resource.

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

AWS API Gateway supports CORS for OPTIONS only when using SAM (without Lambda proxy integration)

I'm using AWS Serverless for building a small site with around 15 Lambda functions.
My Cloudformation stack is completely built using SAM.
I'm NOT using Lambda proxy integration.
The Api section in the SAM yaml template config looks like this:
AppApi:
Type: AWS::Serverless::Api
Properties:
Cors:
AllowMethods: "'*'"
AllowHeaders: "'Content-Type'"
AllowOrigin: "'*'"
...........More Stuff..........
When I deploy this SAM yaml template, I see that my ApiGateway created the OPTIONS verb for all methods and when I shoot a request with the OPTIONS verb, I do see the CORS headers correctly.
The problem is that the other verbs (e.g. POST) don't add those headers to their response as the OPTIONS request did and when I run my api from the browser I get the cross origin policy error in my console.
So my current workaround was to add the CORS header using integrated responses to specific status codes, but I cannot and dont want to handle that for 15 methods and I want to support all response status codes (e.g. 4xx\5xx etc.).
My questions:
Am I doing anything wrong here or is this a SAM bug?
If this is a bug, is there any workaround other from adding the headers using integrated responses (or from my code)?
Is there a way I can add headers "globally" from an Api Gateway? Or support some kind of global integrated responses?
If you are using Lambda with Proxy Integrations, you need to specify the CORS Origin in your HTTP response.
For Lambda or HTTP proxy integrations, you can still set up the
required OPTIONS response headers in API Gateway. 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.
https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html
All responses from Lambda need to have these headers and status code, but you could extract that to a shared library to reduce code duplication. Errors handled by API-G will have the headers added automatically.
You probably already have this, but the NodeJS pattern is like this:
var response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin" : "*"
},
body: JSON.stringify({
someReturnData
})
};
callback(null, response);
If you really don't want to do this then you can turn off Lambda-Proxy Integration, but that means that all request response payloads need to be handled in API-G instead of Lambda. IMO this provides much less flexibility and more configuration required to achieve the same results.
Here is an interesting comparison of the two approaches.

CORS error despite 'Access-Control-Allow-Origin' specified in Lambda response when using Lambda, API Gateway and Cloudfront

I have a deployed web app, it's built with a React & Redux frontend hosted on S3, and a several backend micro-services hosted on AWS Lambda and exposed over API Gateway that were deployed with Serverless. The site is also distributed via a CloudFront CDN.
The micro-services interact with several external services but the primary one I am concerned with is the get-products service which queries the Stripe product database and returns the products to my React app from there.
The site has been working fine until yesterday when I deployed some new (basically cosmetic) changes to front end, and added some more SKUs to the Stripe database. Since adding these changes I have started to experience CORS errors where previously there were none.
Initially I got the following error:
Access to XMLHttpRequest at 'https://XXXXXXXXXX.execute-api.eu-west-1.amazonaws.com/dev/products' from origin 'https://www.superfunwebsite.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
This was despite my Lambda response including the following headers:
'Access-Control-Allow-Origin': '*',
At this point I updated my Lambdas response headers to the following:
'Access-Control-Allow-Origin': 'https://www.superfunwebsite.com/',
'Access-Control-Allow-Credentials': true
The error has largely persisted, except for on one random page load where instead I got an error stating that the Origin https://www.superfunwebsite.com and 'Access-Control-Allow-Origin' https://www.superfunwebsite.com/ didn't match.
I've since changed the Lambda response headers to this:
'Access-Control-Allow-Origin': 'https://www.superfunwebsite.com',
'Access-Control-Allow-Credentials': true
Which seems as though it may have solved that anomalous second error.
However, error one persists even when I am testing using a HTTP client like Insomnia. I get the following in the response.
// status code
403
// response
{
"message": "Forbidden"
}
// headers
Content-Type: application/json
Content-Length: 23
Connection: keep-alive
Date: Tue, 18 Sep 2018 13:22:41 GMT
x-amzn-RequestId: eb691541-bb45-11e8-82ff-6d1b542dffb9
x-amzn-ErrorType: ForbiddenException
x-amz-apigw-id: NaxVLGJgjoEF5Fg=
X-Cache: Error from cloudfront
Via: 1.1 08037e15a3c6f503f39825efeb7f0210.cloudfront.net (CloudFront)
X-Amz-Cf-Id: cbNtb4xKWc48VPFon-Cl9y27KmXRVLIN5SWuYwNWlWsTXeaAXx3z-Q==
Based on the above Insomnia output it seems that my issue is somehow related to CloudFront although I don't understand exactly how. This other S/O post seems to indicate that there should be options for me in the CloudFront behaviours section that would allow me to whitelist headers for the response, however these options are not visible to me in the console, I've seen suggestions this is because the origin is on S3.
My question is simply if anyone knows how I can fix this issue or if there is something else I've not considered that I should be looking at?
Here you have to Whitelist the respective CORS headers in the behaviour section of the cloudfront distribution.
Open your distribution from the Amazon CloudFront console
Choose the Behaviors view.
Choose Create Behavior, or choose an existing behavior and then choose Edit.
For option Cache Based on Selected Request Headers, choose Whitelist.
Under Whitelist Headers, choose headers (Access-Control-Request-Headers and Access-Control-request-methods, Origin) from the menu on the left, and then choose Add.
Choose Yes, Edit.
The above settings should work for you (as its mainly for GET and HEAD) but if it doesn't, enable OPTIONS method as well using below article.
Please check the below article from aws:
no-access-control-allow-origin-error
Custom CORS Cloudfront
enhanced-cloudfront-customization
This issue has now been solved. In the end it was not in fact a CORS issue but a staging one. My query was failing because I was querying an endpoint stage that did not exist.
It appears that in the set up I had using CloudFront and API Gateway, when querying an endpoint that didn't exist I was returned a 403 response without CORS headers.
The browser/CloudFront (not sure which) then identified the absence of CORS headers and threw an error in response to that.
In my case simply changing my endpoint from the dev stage (which did not exist on the associated AWS account associated with the live stage):
https://XXXXXXXXXX.execute-api.eu-west-1.amazonaws.com/dev/products
To the live stage:
https://XXXXXXXXXX.execute-api.eu-west-1.amazonaws.com/live/products
Resolved the issue immediately.

How to restrict a Lambda function to respond only to specific origins?

I want to restrict my Lambda function (created with the Serverless Framework tool) to accept requests only from abc.com and def.com. It should reject all other requests. How can I do this? I tried setting access control origins like this:
cors: true
response:
headers:
Access-Control-Allow-Origin: "'beta.leafycode.com leafycode.com'"
and like this in the handler:
headers: {
"Access-Control-Allow-Origin" : "beta.leafycode.com leafycode.com"
},
but nothing worked. Any idea why?
The issue with your code is that Access-Control-Allow-Origin doesn't accept multiple domains.
From this answer:
Sounds like the recommended way to do it is to have your server read
the Origin header from the client, compare that to the list of domains
you'd like to allow, and if it matches, echo the value of the Origin
header back to the client as the Access-Control-Allow-Origin header in
the response.
So, when writing support to the OPTIONS verb, which is the verb where the browser will preflight a request to see if CORS is supported, you need to write your Lambda code to inspect the event object to see the domain of the client and dynamically set the corresponding Access-Control-Allow-Origin with the domain.
In your question, you have used a CORS configuration for two different types: Lambda and Lamba-Proxy. I recommend that you use the second option, so you will be able to set the domain dynamically.
headers: {
"Access-Control-Allow-Origin" : myDomainValue
},
See more about CORS configuration in the Serverless Framework here.