No 'Access-Control-Allow-Origin' AWS Lambda - node.js

happy new year everybody!
So I have a reactJS API call which looks like this:
async function callApi() {
const requestData = {
headers: {
Authorization: token
}
}
if (calledAPI === false) {
let data = await API
.get('caresyncauthapi' , '/access', requestData)
.then(response => {
let arr = {};
arr = response.Items;
setZorgSearchData(arr)
})
}
calledAPI = true;
}
The Lambda it calls looks like this:
exports.handler = async (event) => {
if (event.requestContext.authorizer) {
const claims = event.requestContext.authorizer.claims;
username = claims['cognito:username'];
}
var params = {
TableName: tableName,
IndexName: "ZorgverlenerID-index",
KeyConditionExpression: "#ZorgverlenerID = :zorg_id",
ExpressionAttributeNames: {
"#ZorgverlenerID": "ZorgverlenerID"
},
ExpressionAttributeValues: {
":zorg_id": username
}
};
try {
data = await dynamodb.query(params).promise();
console.log( "Status code : 200");
console.log(data.Items);
let response =
{
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
},
body: JSON.stringify(data.Items)
}
return JSON.stringify(response);
} catch (error){
console.log( "Status code : 400, Error code : ", error.stack);
}
};
Basically the Lambda gets the context from the sent token (username of the person logged in).
It then queries this database with this username, it then puts the result in a json and returns it.
The await dynamodb.query(params).promise(); gives me an 200 (success) and it also prints the correct data I want to return in cloudwatch.
But when the data returns to my ReactJS application i recieve:
Access to XMLHttpRequest at 'https://qcesrr2td3.execute-api.eu-west-1.amazonaws.com/devl/access' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
xhr.js:187 GET https://qcesrr2td3.execute-api.eu-west-1.amazonaws.com/devl/access net::ERR_FAILED 502
dispatchXhrRequest # xhr.js:187
xhrAdapter # xhr.js:13
Thing I already tried:
Enabling CORS in the API gateway
Changing up the headers in the response
Returning the response without JSON.stringify, its null then
Does anybody have an idea what I can still change?

Fixed it by not stringifying my response:
try {
data = await dynamodb.query(params).promise();
console.log( "Status code : 200");
console.log(data.Items);
let response =
{
statusCode: 200,
headers: {
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods" : "OPTIONS,POST,GET,PUT"
},
body: JSON.stringify(data)
}
return response;
} catch (error){
console.log( "Status code : 400, Error code : ", error.stack);
}
Weird because this code worked for a week then suddenly stopped. Oh well it works again.

Related

Amazon SP-API FeedsAPI createFeedDocument Invalid contentType error / Node.js

I am using Node.js and Express.js to make requests to Amazon SP-API.
Unfortunately, I constantly get either socket hangup or just no response when trying to perform the Feeds API createFeedDocument call.
Here is my 2 Steps of code:
Step 1: Authorize SP-API
// environment variables are being used when process.env.XXXX is present...
exports.authorizeAmazonSpAPI = async ( req, res ) =>
{
let a = {}
let { _id } = req.body
a._id = _id // PARAM
try {
a.user_doc = await User.findById( a._id )
a.client_id = process.env.SELLING_PARTNER_APP_CLIENT_ID
a.client_secret = process.env.SELLING_PARTNER_APP_CLIENT_SECRET
a.grant_type = `refresh_token`
let { amazon_refresh_token } = a.user_doc
a.amazon_refresh_token = amazon_refresh_token // PARAM
a.endpoint = `https://api.amazon.com/auth/o2/token?grant_type=${ a.grant_type }&refresh_token=${ a.amazon_refresh_token }&client_id=${ a.client_id }&client_secret=${ a.client_secret }`
a.method = `POST`
a.headers = {
Accept: "application/json",
"Content-Type": "application/json"
}
a.access_token_response = await fetch( a.endpoint, { method: a.method, headers: a.headers } )
a.access_token_response = await a.access_token_response.json()
a.amazon_access_token = a.access_token_response.access_token
a.saved_user = await User.findByIdAndUpdate( a._id, { amazon_access_token: a.amazon_access_token } )
let {
Credentials: {
AccessKeyId,
SecretAccessKey,
SessionToken,
}
} = await STS.assumeRole({
RoleArn: `arn:aws:iam::XXXXX:role/userrole`, // YOUR IAM ROLE ARN
RoleSessionName: 'sp-api',
}).promise()
// a.Credentials = Credentials // PARAM
a.AccessKeyId = AccessKeyId // PARAM
a.SecretAccessKey = SecretAccessKey // PARAM
a.SessionToken = SessionToken // PARAM
res.json( {
marketplaceId: "ATVPDKIKX0DER",
access_token: a.amazon_access_token,
tempCreds: {
AccessKeyId: a.AccessKeyId,
SecretAccessKey: a.SecretAccessKey,
SessionToken: a.SessionToken,
}
} )
} catch ( e ) {
log( `authorizeAmazonSpAPI e: `, e )
res.status( 400 ).json( { error: e } )
}
}
output:
{
"marketplaceId": "ATVPDKIKX0DER",
"access_token": "Atza|...",
"tempCreds": {
"AccessKeyId": "XXXXXXXXX",
"SecretAccessKey": "XXXXXXXXX",
"SessionToken": "XXXXXXXXX"
}
}
STEP 2: (using output from above) Performing the Feeds API createFeedDocument call
exports.createFeedDocument = async ( req, res ) =>
{
let { marketplaceId, access_token, tempCreds } = req.body;
try {
const {
AccessKeyId,
SessionToken,
SecretAccessKey,
} = tempCreds;
const {
aws_region,
end_point,
} = regions.get( marketplaceId );
const params = {
path: `/feeds/2021-06-30/documents`,
method: 'POST',
body: JSON.stringify( { contentType: "text/xml; charset=UTF-8" } ),
host: end_point,
region: aws_region,
service: 'execute-api',
headers: {
'User-Agent': 'MyAmazonApp/1.0 (Language=JavaScript;)',
'x-amz-access-token': access_token,
"Content-Type":"Content-Type=application/json"
},
}
let signed_result = aws4.sign(params, {accessKeyId: AccessKeyId, secretAccessKey: SecretAccessKey, sessionToken: SessionToken})
log( `signed_result: `, signed_result )
res.json( signed_result )
} catch ( e ) {
log( `createFeedDocument e: `, e )
res.status( 400 ).json( { error: e } )
}
}
There is no output I get when making the STEP 2 call. It just hangs then does socket hang up.
What am I doing wrong?
Previously I tried changing the body parameter ( where it says it wants contentType in the docs ) but that so far did not work.
Here is specifically what did not work for me with contentType in the params going into aws4.sign...:
body: JSON.stringify( { contentType: "text/xml; charset=UTF-8" } ), - results in "hanging" behavior where eventually leading to socket hang up error.
body: { contentType: "text/xml; charset=UTF-8" }, - results in error... TypeError [ERR_INVALID_ARG_TYPE]: The "string" argument must be of type string or an instance of Buffer or ArrayBuffer. Received an instance of Object
contentType: "text/xml; charset=UTF-8", - results in this fabulous error... { "error": { "statusCode": 400, "res": { "errors": [ { "code": "InvalidInput", "message": "One or more required parameters missing", "details": "contentType;" } ] } } }
How is this call supposed to be made correctly?
Can anyone give any successful example please?
It would be highly appreciated!

How to post a body correctly in Postman

I try to make a post request to an api but, api returns error. Although I thought about it for a long time, I could not find the cause of the error.
I think the error is due to not creating the body correctly because when I send empty array as items array the code works.
API DOCUMENTATION:
Here is my request:
module.exports.createOrder = (token, paymentId, items, callback) => {
const url = urlBase;
request(
{
url: url,
method: "POST",
json: true,
headers: {
"content-type": "application/json",
Authorization: `Bearer ${token}`,
},
body: {
secretKey: `${secretKey}`,
paymentId: paymentId,
items: items,
},
},
(error, response) => {
if (error) {
Sentry.captureException(error);
callback(errMessage, undefined);
} else {
const data = response.body;
callback(undefined, data);
}
}
);
};
Here is test values:
const testToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYyM2RmYjg4NDA4M2IwMDAyNDNjYzRhNyIsImlhdCI6MTY0ODIyOTI1NywiZXhwIjoxNjQ4MzE1NjU3fQ.9fgR3ei81vsHVMhSi8VwEyE2WMgFIMthm0PF9_zrqjw"
const paymentId = "pi_1Dq2f62eZvKYlo2Cy1moIb0G"
const variant = {
variations_values: {
color:'Black',
size:'42'
},
price:495,
product_id: "883360511078"
}
const productId = "82936941"
const quantity = 1
const item = {
variant:variant,
productId:productId,
quantity:quantity
}
let items = [item]
orderRequests.createOrder(testToken, paymentId, items, (err, data) => {
if(data){
console.log(data)
} else{
console.log(err)
}
})
I get internal server error as a response when I post these test values, but If I post an empty array as items, api does not return internal server error. What is the problem, any help?
Internal Server Error Response when I send an array with post body:
Expected request when I send an empty array with post body:

AWS Preflight response blocked by CORs for PUT method

Here's the actual error
Access to XMLHttpRequest at 'https://uzk3crusd9.execute-api.us-east-2.amazonaws.com/production/contact' from origin 'https://seb-contact-form.netlify.app' has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.
It runs fine locally, but when I went to connect it to Netlify, this error has appeared.
my lambda function:
const AWS = require('aws-sdk');
AWS.config.update({
region: 'us-east-2'
});
const dynamodb = new AWS.DynamoDB.DocumentClient();
const dynamodbTableName = 'contact-form';
const contactPath = '/contact';
exports.handler = async function(event) {
console.log('Request event: ', event);
let response;
switch(true) {
case event.httpMethod === 'POST' && event.path === contactPath:
response = await createContact(JSON.parse(event.body));
break;
default:
response = buildResponse(404, '404 Not Found');
}
return response;
}
async function createContact(requestBody) {
const params = {
TableName: dynamodbTableName,
Item: requestBody
}
return await dynamodb.put(params).promise().then(() => {
const body = {
Operation: 'SAVE',
Message: 'SUCCESS',
Item: requestBody
}
return buildResponse(200, body);
}, (error) => {
console.error('Oh no! Something went wrong...', error);
})
}
function buildResponse(statusCode, body) {
const response = {
statusCode,
headers: {
'Content-Type': 'application/json',
"Access-Control-Allow-Headers" : "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token",
"Access-Control-Allow-Methods" : "OPTIONS,POST,PUT,PATCH",
"Access-Control-Allow-Credentials" : true,
"Access-Control-Allow-Origin" : "*",
"X-Requested-With" : "*"
},
body: JSON.stringify(body)
}
return response;
}
I realize that there's a mix between using the post and put methods here. But it worked fine locally, and in some youtube tutorial.
There are two steps:
Enable support for CORS in your Lambda (which you already did).
Enable CORS support in the API Gateway endpoint itself (which I believe you didn't do).
https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors-console.html

Node JS Request Printing nothing in console

I'm making an post call using the nodejs requests module. However, the console.log statements seems to be not working for either the error or the response.body that I am trying to get.
My POST request needs the following headers -
Accept : "application/json"
Content-Type : "application/json"
Authorization : Basic + Base64Encoded(username+password)
The post body is something like this
Body:
{
"arg_1" : "a_string_key"
, "arg_2" : "a_string"
, "arg_3" : "a_string"
, "arg_4" : "some_value"
, "arg_5" : "some_string"
, "arg_6" : "<yyyy-mm-dd>"
, "arg_7" : "<yyyy-mm-dd>"
}
My code does nothing but send a POST request and checks if the response.statusCode ==200
Here is what I am doing
var int_user = "username";
var int_pass = "password";
var encoding = "base64"
var auth = "Basic" + new Buffer(int_user + int_pass).toString(encoding);
var headers = {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": auth
}
var options = {
url: 'URL_I_WANT',
// method: 'POST',
headers: headers,
body : {
"arg_1": "a_string_key",
"arg_2": "a_string",
"arg_3": "a_string",
"arg_4": "some_value",
"arg_5": "some_string",
"arg_6": "<yyyy-mm-dd>",
"arg_7": "<yyyy-mm-dd>"
},
json: true
}
console.log('Before request');
request.post(options, function(error, response) {
if (error) {
console.log(error);
}
try {
if (!error && response.statusCode == 200) {
console.log(response.body);
console.log('Success');
}
} catch (error) {
console.log(error)
}
});
console.log('After request');
The code runs without any glitch and I get the before and after request console statements. However the statements inside the requests do not appear in the console, which means my request is not going through. I am not able to understand this. Shouldn't an error come if there is an issue with the request itself? Any if the request is failing, why isn't the error printed out?
This could be because your node process is auto-closed and it will exit before the async request to finishes (haven't looked into it but it might be something configurable). I've seen such set-up on repl.it for example.
To overcome this(if not configurable), you could wrap your code in an async function and use the request-promise to call await request.
var request = require('request-promise-native');
var int_user = "username";
var int_pass = "password";
var encoding = "base64"
var auth = "Basic" + new Buffer(int_user + int_pass).toString(encoding);
var headers = {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": auth
}
var options = {
url: 'https://google.com',
method: 'POST',
headers: headers,
body : {
"arg_1": "a_string_key",
"arg_2": "a_string",
"arg_3": "a_string",
"arg_4": "some_value",
"arg_5": "some_string",
"arg_6": "<yyyy-mm-dd>",
"arg_7": "<yyyy-mm-dd>"
},
json: true
};
console.log('Before request');
async function main() {
try {
const response = await request.post(options);
if (response.statusCode === 200) {
console.log(response.body);
console.log('Success');
process.exit();
}
console.log(`Bad statusCode:${response.statusCode}`);
}
catch (error) {
console.log(error);
}
}
main();
you can check-out the code below
a link to the code above, working on repl.it

Google nocaptcha post from server to siteverify says details are missing

[ { hostname: 'www.google.com',
path: '/recaptcha/api/siteverify',
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Content-Length': 556 } },
'{"secret":"XXXUseThisForCommunicationBetweenYourSiteAndGoogleXXX","response":"03AHJ_VuusXdr5IdGpNzQPRjedGs-Le066Fx9r-Lk1gIfLqlzwxapPx70_LukmcOsw3x-m2DSfpvQVylx060H9IjFP82fy7505_t_rjSivauiwBUyQPrBMp5kTRviq_DD1L2mVMTTrBieUMlQM69AIuG3KwmdOQMyMJS2iJdRuRNnvAmDlPSejkASR4X-7c4IIP3NoMb52Qsl9QPeU6kGaPtxqmf1IpNwbSC3bzLXQD-QV1aI4GgaeqSPfOO8EPfISJMQ5kbCd9wqAwHqDAXMtNSvz10Ty30R71HqmsSk7YHddFQhei1L6y9j7nxnY5QtAxHehhpYwJVNjI96hxeIaG58_CQHGbAufy4aPGAlf-zJ6be_Xtdzd4AnHxiX9OuCKQI8eQlh6DZLGaymxXDmPNu4TijGyyu0VeTPTTKf12zVUg86_0ZmszWZDtALjnNnxBH7bZqrgWXhy","remoteip":"00.00.000.000"}' ]
If I post that and google returns this:
{ success: false, 'error-codes': [ 'missing-input-response', 'missing-input-secret'] }
I don't see what is happening wrong
https://www.google.com/recaptcha/admin#site/XXXXXX?setup says:
When your users submit the form where you integrated reCAPTCHA, you'll get as part of the payload a string with the name "g-recaptcha-response". In order to check whether Google has verified that user, send a POST request with these parameters:
URL: https://www.google.com/recaptcha/api/siteverify
secret(required) - XXXXXX,
response(required) - The value of 'g-recaptcha-response',
remoteip - The end user's ip address.
I have clearly sent all these things! What could be happening here? The error does not say they are wrong, it says they are 'missing'
And the above Quoted text from google clearly says POST not GET Google reCAPTCHA: how to get user response and validate in the server side
But if I try a GET request then the response is [ null, 400, undefined ]
UPDATE
As #mscdex pointed out application/x-www-form-urlencoded is required but the responce still said that it was missing the secret so I url encoded this instead as I figured something bad may be happening to the item at the start of the object.
{'_':'_','secret':'XXXXXX','response':'whateverXXYsgTSG','remoteip':'00.00.000.000'}
And finally it worked:
[ { hostname: 'www.google.com',
path: '/recaptcha/api/siteverify?',
method: 'POST',
headers:
{ 'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': 548 } },
'"_=_&secret=XXXXXX&response=03AHJ_VuurQFgsftybLlvrdGOwXfNneWp4v7FPJJbOD9CGpiHAkFBaiNy7YWXcHrAkU6SPU5UZpgKCptU3gRX5OPqXEh2qqP3nXJpiBWoxFW_Iv05P2UA23rzzZk0ecScmMSL1PP1uyBCdJ08HpAWEuz2PzL6m6u71k09xQbVbPZ5KT6qnb-mdPNyEkdBxtc9a5oYpnOoHg7ax6q4Ms4Lis4qrNBLCavKmYZ6vAmYitSEI0a0GERnlI3wLSvayhc-Yygv1koKIjg2q8GHXV1UhKLzBa8t8x2ibRBNwXUMBFs3Qj_lfwgiTNtIaU3kEAFPULJulZDOsAcovKpjk5xkyMM2C5YDGYMioeyOMl9ZmyyvkwfrrRe8e9o_tD6SaTTSAcrcxsfYGm-w0_CDbsa2IWSkjiMN-2B9SClOZJGXXVXVIuIYClIK3XuUvTsObCzxJAq2IKwwMTtYX&remoteip=00.00.000.000"' ]
[ { success: true }, 200, undefined ]
But I would like to do this properly not hacky So if anyone can answer how to do it properly that would be swell!
var JSON={
https:require('https')
, toquery:require('../node_modules/querystring').stringify
, stringify:require('../node_modules/json-stringify-safe')
, parse:require('../node_modules/try-json-parse')
, get:function(url,callback){process.env.NODE_TLS_REJECT_UNAUTHORIZED="0";var req=JSON.https.request(url,function(res){var buffer='';res.setEncoding('utf8');res.on('data',function(chunk){buffer+=chunk;});res.on('end',function(){try{var data=JSON.parse(buffer);callback(data,res.statusCode);}catch(e){console.log(e);}});});req.end();}
, post:function(url,path,data,type,callback){if(!callback){callback=type;type=undefined;}data=JSON.stringify(data);var options={hostname:url,path:path,method:'POST',headers:{'Content-Type':type||'application/json','Content-Length':data.length}};console.dir([options,data]);var req=JSON.https.request(options,function(res){var buffer='';res.setEncoding('utf8');res.on('data',function(chunk){buffer+=chunk;});res.on('end',function(){try{var data=JSON.parse(buffer);callback(data,res.statusCode);}catch(e){console.log(e);}});});req.write(data);req.end();}
};
JSON.post(
'www.google.com'
, '/recaptcha/api/siteverify?'
, JSON.toquery({'_':'_','secret':'XXXX','response':response,'remoteip':remoteip})
, 'application/x-www-form-urlencoded'
, function(data,result,statusCode){
console.dir([data,result,statusCode]);
if(result.success){}
else{}
});
Here is how I do it in one of my project using superagent. This is my recaptcha-helper.js
var request = require("superagent");
var config = {
recaptcha: {
secret: "XXXXX",
url: "https://www.google.com/recaptcha/api/siteverify",
},
};
var ERROR_CODES = {
"missing-input-secret": "Unexpected Server Error (1)",
"invalid-input-secret": "Unexpected Server Error (2)",
"missing-input-response": "Missing reCAPTCHA value",
"invalid-input-response": "Invalid reCATPCHA value",
};
exports.getErrorCode = function (errorCode) {
if (Array.isArray(errorCode)) {
var errors = errorCode.map(function (code) {
return exports.getErrorCode(code);
});
return errors.join("\n");
}
return ERROR_CODES[errorCode] ||
(errorCode ? ("Unexpected reCAPTCHA error: " + errorCode) : "Unexpected reCAPTCHA error");
};
exports.parseResponse = function (err, res) {
if (err) {
return { success: false, error: err };
} else if (!res.body.success) {
var error = new Error(exports.getErrorCode(res.body["error-codes"]));
return { success: false, error: error };
} else {
return { success: true };
}
};
exports.verify = function (response, ip) {
if (process.env.NODE_ENV === "test") {
return response ? Promise.resolve() :
Promise.reject(new Error("Test reCAPTCHA Error"));
}
return new Promise (function (resolve, reject) {
request.post(config.recaptcha.url)
.type("form")
.accept("json")
.send({
secret: config.recaptcha.secret,
response: response,
remoteip: ip,
})
.end(function (err, res) {
var parsedRes = exports.parseResponse(err, res);
return parsedRes.success ? resolve() : reject(parsedRes.error);
});
});
};
And you can use it doing
var captchaHelper = require('./recaptcha-helper');
captchaHelper.verify(req.body.captcha, req.ip)
.then(function () {
// on success
}).catch(function (err {
// on error
});
I solved this by passing secret and response as a query parameter:
Example :
axios.post("https://www.google.com/recaptcha/api/siteverify?secret="
+ 'XXXXXX' + "&response=" + response + "&remoteip=" + req.connection.remoteAddress);

Resources