Google Cloud Function CORS error when making POST request - node.js

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.

Related

Axios bad request status 400

I'm having this issue AxiosError: Request failed with status code 400
I checked the console and I test manually the url and It worked, so I don't know what's wrong, this code:
//file controller.js
//Set Create Session
exports.setSession = async (req, res) => {
const data = await request({
path: process.env.APP_LOCALHOST_URL + urlLogin.setCreateSession,
method: 'POST',
body: JSON.stringify(req.body)
});
return res.json(data);
}
//file request.js
exports.request = async ({path, method = "GET", body }) => {
try {
const response = await axios({
method: method,
url: path,
headers: {
'Content-Type': 'application/json'
},
body: body
});
return response;
} catch (error) {
console.log("error: ", error);
}
}
the function setSession is to call in my routes file, and the function request is my reusable component. My intention is to use the function request in many functions, these could be of the GET, DELETE, PUT, POST, PATCH type.
So, currently I get this on console:
data: {
error: '5',
errorId: 'badRequest',
errorString: 'Internal error: Undefined JSON value.'
}

Empty request body in server, Object object into DB

I'm using NodeJS+express and React. The request body's expected output is "tipologia", but it actually returns an empty object.
I have looked for similar questions (there a lot of them) but none of these is useful.
client:
function CreateStudyPlan(tipologia){
return new Promise((resolve, reject) => {
fetch((URL+'/PianoStudio'), {
method: 'POST',
credentials: 'include',
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "*"
},
body: JSON.stringify(tipologia),
}).then((response) => {
if (response.ok) {
resolve(null);
} else {
// analyze the cause of error
response.json()
.then((message) => { reject(message); }) // error message in the response body
.catch(() => { reject({ error: "Cannot parse server response." }) }); // something else
}
}).catch(() => { reject({ error: "Cannot communicate with the server." }) }); // connection errors
});
}
server:
// set-up the middlewares
app.use(morgan('dev'));
app.use(express.json());
const corsOptions = {
origin: 'http://localhost:3000',
credentials: true,
};
app.use(cors(corsOptions));
const isLoggedIn = (req, res, next) => {
if(req.isAuthenticated())
return next();
return res.status(401).json({ error: 'not authenticated'});
}
app.post('/PianoStudio', isLoggedIn, async (req, res) => {
try {
await dao.createPianoStudio(req.body, req.user.id);
res.status(201).json(req.body);
} catch(err) {
console.log(err);
res.status(503).json({error: `Database error during the creation of piano di studi for user ${req.user.id}.`});
}
});
The problem is that req.body is empty and should not be ( i am expecting it to output part-time):
The insert into the DB shows that req.user.id is ok, while req.body is an empty Object:
--
2 WORDS ON REQUEST ID AND BODY:
req.body should be the
body: JSON:Stringify(tipologia)
from the client, while req.user.id is retrieved by the session through the isLoggedIn.
2 WORDS ON HEADERS:
At first i had
headers: {
'Content-Type': 'application/json',
But it gave me CORS error:
Access to fetch at 'http://localhost:3001/PianoStudio' from origin 'http://localhost:3000' has been blocked by CORS policy: 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.
So i changed the Headers to
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "*"
}
as
putting 'Content-Type': 'application/json', returns again CORS error.
You should try to define tipologia as an object, in the Client:
body: JSON.stringify({tip_str: tipologia})
While in your Server, you will retrieve your tipologia as follows:
dao.createPianoStudio(req.body.tip_str, req.user.id)

Response to preflight request doesn't pass access control check: It does not have HTTP ok status. - Google Cloud Function, Vue3

I am attempting to make a web scraper using Google Cloud Functions. The GCF works as expected when fed input on the GCF test function area, however when triggering the GCF with an HTTP request from my clientside (coded in Vue3), I am getting the following CORS error: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
This is my HTTP request from the client-side,
const response = await fetch(url, {
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
"Allow-Control-Allow-Origin": "*",
},
body: JSON.stringify({ input }),
});
// console.log(response);
const data = await response.json();
GCF Code
const puppeteer = require("puppeteer");
exports.webScrape = async (req, resp) => {
resp.header("Access-Control-Allow-Origin", "*");
resp.header("Access-Control-Allow-Credentials", "true");
resp.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
resp.header(
"Access-Control-Allow-Headers",
"Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers"
);
const input = req.body.input;
async function scrapeSite(url) {
//it does stuff here
}
try {
const response = await scrapeSite(input);
const data = await JSON.stringify(response);
resp.send(data);
} catch (error) {
console.log("server is catching the error");
console.log(error);
resp.status(404).send({
ErrorMessage: "An invalid URL was given to the input box, please check the input and try again",
error,
});
}
return;
};

Proxy API request through Express return pending Promise instead of response

I am currently trying to work with the Atlassian Jira rest API. In order to not get a CORS error I go through the recommended route of not sending the request from the browser but proxy it through my express server.
Now as I am doing this, all I receive back in the app is a pending promise. I assume that I have not correctly resolved it at one point but I cant figure out where.
API Handler sending the request to the proxy:
const baseURL = `${apiConfig}/jiraproxy`;
export const testConnection = integration => {
return fetch(`${baseURL}/get`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(integration)
})
.then(handleResponse)
.catch(handleError);
};
Jira Proxy Endpoint on the Express Server
const baseURL = `rest/api/3/dashboard`;
router.post("/get", (req, res) => {
fetch(req.body.link + baseURL, {
method: "GET",
headers: { Accept: "application/json" },
auth: {
username: req.body.credentials.username,
password: req.body.credentials.token
}
})
.then(handleResponse)
.catch(handleError);
});
handleResponse & handle Error Methods:
async function handleResponse(response) {
if (response.ok) {
return response.json();
}
if (response.status === 400) {
const error = await response.text();
throw new Error(error);
}
throw new Error("Network response was not ok.");
}
function handleError(error) {
// eslint-disable-next-line no-console
console.error(`API call failed. ${error}`);
throw error;
}
Goal:
Send the request of sending a request to the proxy and return the resonse of the proxy as the return of the initial "testConction" method.
Error:
No errors thrown, but the response received in the Browser is a pending promise.
Change to the Jira Proxy router fixed it. Thanks to #jfriend00.
router.post("/get", (req, res) => {
return fetch(req.body.link + baseURL, {
method: "GET",
headers: { Accept: "application/json" },
auth: {
username: req.body.credentials.username,
password: req.body.credentials.token
}
})
// This is the part that changed
.then(response => handleResponse(response))
.then(jiraResponse => res.status(200).json(jiraResponse))
.catch(handleError);
});

POST request to restify with Fetch API not working

I'm having some major problems understanding why the Fetch API won't let me send a POST request to my restify server.
I have a basic restify server with a route that receives POST requests on /login.
This route works perfectly as intended if I test with Postman or HTTPRequester, but when I then test it on a browser application with the fetch API, I get the following error (in Chrome):
OPTIONS http://localhost:1337/login 405 (Method Not Allowed)
Fetch API cannot load http://localhost:1337/login. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. The response had HTTP status code 405. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Two issues
I specifically use the POSt method in my request, so why suddenly OPTIONS?
I've already set Access-Control-Allow-Origin: * on my server.
EDIT: I use restify v5.2.0
My server app:
const restify = require('restify');
const app = restify.createServer({
'name': 'API Token Test',
'version': '1.0.0'
});
app.use(restify.plugins.acceptParser(app.acceptable));
app.use(restify.plugins.bodyParser());
app.use(restify.plugins.jsonp());
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'X-Requested-With');
return next();
});
app.post('/login', (req, res) => {
db.execute('SELECT idusers, password FROM users WHERE username = ?', [req.body.username], (selError, rows) => {
if (passwordHash.verify(req.body.password, rows[0].password)) {
crypto.randomBytes(256, (err, buf) => {
if (err) return res.status(500).end();
else {
const token = buf.toString('hex');
db.execute('INSERT INTO accesstokens SET userid = ?, token = ?', [rows[0].idusers, token], (insError) => {
if (insError) return res.status(500).end();
else return res.send({ "AccessToken": token });
});
}
});
} else {
res.status(401).end();
}
});
});
app.listen(1337);
(I've left out mysql stuffs and crypto/password-hash -requires, which are irrelevant to the problem)
And my clientside script:
(() => {
document.addEventListener('DOMContentLoaded', () => {
const form = document.querySelector('.loginForm');
form.onsubmit = () => {
const data = JSON.stringify({
'username': form.username.value,
'password': form.password.value
});
let headers = new Headers();
headers.set('Accept', 'application/json');
headers.set('Content-Type', 'application/json');
headers.set('Content-Length', data.length);
fetch('http://localhost:1337/login', {
'method': 'POST',
'headers': headers,
'mode': 'cors',
'cache': 'default',
'body': data
})
.then((result) => result.json())
.then((data) => {
console.log(data);
localStorage.setItem('token', data);
})
.catch((err) => {
console.log(err);
});
return false;
};
});
})();
Appears all CORS support has been moved to this module since restify v5.x.
Installing restify-cors-middleware and adding the following to my app worked:
const corsMiddleware = require('restify-cors-middleware');
const cors = corsMiddleware({
'origins': ['*']
});
app.pre(cors.preflight);
app.use(cors.actual);

Resources