AWS Preflight response blocked by CORs for PUT method - node.js

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

Related

Cannot save email in React.js + Express

So I have a function getProfile(). Inside it I am trying to fetch users from the server using a POST call, however it's throwing a CORS error in its response.
Here is the getProfile:
const getProfile = async () => {
try {
const res = await fetch("http://localhost:5000/dashboard/", {
method: "POST",
headers: {
jwt_token: localStorage.token,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const parseData = await res.json();
setEmail(parseData.email);
console.log("Try success");
console.log(parseData.email);
} catch (err) {
console.log("Try failed");
console.error(err.message);
}
};
if you are handling your nodeServer by your own you need to add CORS package
please check this and follow their docs
https://www.npmjs.com/package/cors
1.install package
2. in your app.js file add following
var cors = require('cors')
app.use(cors())
and change you frontend function getProfile() to following:
const getProfile = async () => {
let token = localStorage.getItem("token");
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Authorization", "bearer" + token);
try {
await fetch("http://localhost:5000/dashboard/", {
method: "POST",
headers: myHeaders,
})
.then((r) => r.json().then((data) => ({ status: r.status, data })))
.then((obj) => obj.status === 200 && console.log("data", obj.data));
} catch (err) {
console.error("Try failed", err.message);
}
};

No 'Access-Control-Allow-Origin' AWS Lambda

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.

NodeJS - CORS issue with Google Cloud Storage Signed URLs

I want to enable artist clients to upload images to Cloud Storage using Signed URLs.
The following backend script is used to get the signed url and it is behind a Google Cloud load balancer:
(Does that (load balancer) affect anything?)
exports.getSignedUrl = async (req, res) => {
if (req.method !== 'POST') {
// Return a "method not allowed" error
return res.status(405).end();
}
try {
console.log("Getting signed url..");
const storage = new Storage();
const bucketName = 'bucket' in req.body ? req.body.bucket : 'gsk-public';
// Get a reference to the destination file in GCS
const file = storage.bucket(bucketName).file(req.body.filename);
// Create a temporary upload URL
const expiresAtMs = 'expires' in req.body ? req.body.expires : Date.now() + 100; // Link expires in 1 minute
const configuration = {
version: 'v4',
action: req.body.action,
expires: expiresAtMs
};
if ('type' in req.body) {
configuration.contentType = req.body.type;
}
console.log("Got signed url!");
const url = await file.getSignedUrl(configuration);
console.log(url);
return res.send(url[0]);
}
catch (err) {
console.error(err);
return res.status(500).end();
}
};
CORS setting:
[
{
"origin": [
"https://localhost:3000/",
"https://dreamlouvre-git-development-samuq.vercel.app/"
],
"responseHeader": "*",
"method": ["GET", "PUT", "POST", "OPTIONS", "HEAD"],
"maxAgeSeconds": 3600
}
]
Next.js API endpoint to get the signed url:
import axios from 'axios';
import {config} from '../config';
import { fileTypeToJPG, createQueryString } from '../../../helpers/helpers';
import mime from 'mime';
export default async (req, res) => {
if(req.method === 'POST'){
try{
if(!config.mimeExtensions.some(el => el===mime.getExtension(req.body.type))){
//throw new Error('The file type of your image is not supported. Following types are supported: jpg, png, bmp and tiff.');
return res.status(500).json({ statusCode: 500, message: 'Unsupported filetype.' });
}
//console.log('file name: ', req.body.filename);
//console.log('auth header: ', req.headers.authorization);
const response = await axios.post(`${config.apiBaseUrl}/auth/signedUrl`, req.body, {headers:{"authorization": `Bearer ${req.headers.authorization}`}});
//console.log("Respdata");
//console.log(response.data);
//console.log(response.data[0]);
const respData = {
filename: fileTypeToJPG(req.body.filename),
originalFileName: req.body.filename,
url: response.data
};
return res.status(200).json(respData);
}catch (err) {
//console.log(err);
return res.status('status' in err ? err.status : 500).json({ statusCode: 500, message: err.message });
}
}
else return res.status(403);
};
Next.js Front-end code to put to signed url:
const badi = {
type: file.type,
filename: fileName,
action: "write",
bucket: config.bucketOriginal,
};
console.log("upload 3");
const resp = await axios.post(`/api/auth/getSignedUrl`, badi, {
headers: { authorization: token },
});
console.log("upload 4", resp.data);
await axios.put(resp.data.url, file, {
headers: { "Content-type": file.type },
});
Observed behaviour: Access has been blocked by CORS policy.
Console logs upload 4 and the signed url, but the signed url doesn't work.
Expected behaviour: put request would work correctly.
There was an error in expiry time that probably caused the CORS error.
I fixed this:
const expiresAtMs = 'expires' in req.body ? req.body.expires : Date.now() + 100;
to this:
const expiresAtMs = 'expires' in req.body ? req.body.expires : Date.now() + 15*60*1000;
EDIT: The code already started to work, but is now throwing 403 erros, claiming there's a malformed content-type header although there is not. Content-type header is present in the request.

Google Cloud Function CORS error when making POST request

I can't enable CORS in GCF, allUsers is enabled. Here's my code following this post recommandations
I make a POST call with fetch and JSON as body.
My server supposed to handle the request by performing a reCaptcha verification.
Then respond based on the reCaptcha score.
Thing is I can't even make the request, my server returns status 500.
Emails are sent when sent with 'mode : no-cors'.
exports.contactSendmail = (req, res) => {
res.set('Access-Control-Allow-Origin', '*');
if (req.method === 'OPTIONS') {
/* handle preflight OPTIONS request */
res.set('Access-Control-Allow-Methods', 'GET, POST');
res.set('Access-Control-Allow-Headers', 'Content-Type, Accept');
// cache preflight response for 3600 sec
res.set('Access-Control-Max-Age', '3600');
return res.status(204);
}
const { message, token, email } = JSON.parse(req.body);
console.log(message, token, email);
// Load Node native HTTPS package
const https = require('https');
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const recapatchaKeys = {
secret: `myhiddensecretkey`,
response: token,
};
const urlPath = `/recaptcha/api/siteverify?secret=${recapatchaKeys.secret}&response=${recapatchaKeys.response}`;
const recaptchaOptions = {
hostname: 'google.com',
// port: 443,
path: urlPath,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': 0,
},
};
const reqRecaptcha = https.request(recaptchaOptions, (recaptchaResponse) => {
console.log(`reCaptcha statusCode: ${recaptchaResponse.statusCode}`);
recaptchaResponse.on('data', (d) => {
process.stdout.write(d);
const recapatchaRes = JSON.parse(d);
if (recapatchaRes.score > 0.7) {
const msg = {
to: process.env.CONTACT_EMAIL_RECIPIENT,
from: email,
subject: 'Nouveau contact',
text: message,
// html: "<strong>Its too simple to send mail</strong>"
};
//ES8
(async () => {
try {
await sgMail.send(msg);
res.status(200).send('Email sent');
console.log('Email sent !');
} catch (err) {
console.error('Error with Sendgrid' + err.toString());
}
})();
} else {
res.status(403).send('Forbidden to send Email');
console.log('Forbidden to send Email');
}
});
});
reqRecaptcha.write('');
reqRecaptcha.end();
};
Here's my front call
const response = await fetch(process.env.CONTACT_SENDMAIL_URL, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(emailBody),
});
Any help would be appreciated
As mentioned in the comments, you are not handling any authentication in there. The Documentation states that:
you can either deploy it with the --allow-unauthenticated flag, or use the Console to grant the Cloud Functions Invoker role to allUsers. Then handle CORS and authentication in the function code.
And in order to handle authentication for end users you can in your code you can follow the instruction in this other piece of documentation, which is quite detailed.

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

Resources