Firebase Cloud Functions CORS issue - javascript

I have CORS installed for firebase cloud functions.
I'm using sendgrid to send a transactional email.
I was using this EXACT code in another folder and this function was returning no problem - but I merged 2 separate documents of functions into one and now I keep getting the error...
Access to XMLHttpRequest at 'cloud function trigger' from origin 'my website' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Really confused as to what it could be...
const cors = require('cors')({ origin: true });
exports.sendRequest = functions.https.onRequest((req, res) => {
cors(req, res, () => {
return Promise.resolve()
.then(() => {
if (req.method !== 'POST') {
const error = new Error('Only POST requests are accepted');
error.code = 405;
throw error;
}
const msg = {
to: req.body.companyEmail,
from: req.body.name,
text: 'and easy to do anywhere, even with Node.js',
html: '<strong>and easy to do anywhere, even with Node.js</strong>'
}
};
return sgMail.send(msg);
})
.then((response) => {
if (response.body) {
res.send(response.body);
} else {
res.end();
}
})
.catch((err) => {
console.error(err);
return Promise.reject(err);
});
})
})

Related

How to Fix Headers Already Sent to Client in NodeAPI with Angular

I am getting the following error on my node api, which is really just console logging the request at this point.
router.get('/booksByISBN', checkRole, async (req, res) => {
console.log(req.params)
return res.sendStatus(200);
});
node:internal/errors:484
ErrorCaptureStackTrace(err);
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I believe the issue is because of pre-flight CORS data, but no clue how to fix it.
There is one API call in my Angular 15 application, but upon inspection of the Network tab, I see two api calls are actually being made to my endpoint.
I understand this is because of the CORS options request, but I don't know how to fix it to let the API go through.
CheckRole function
var checkRole = async function CheckRoleAuth (req, res, next) {
try {
const token = req.headers.authorization.split(' ')[1];
const decodedToken = jwt.verify(token, envs.jwtSecret);
await User.findById(decodedToken.userId)
.then(foundUser => {
if (foundUser) {
if (foundUser.role != null || foundUser.role != '') {
if (foundUser.role.includes('Admin'))
{
req.userData = {
email: decodedToken.email,
id: decodedToken.id
};
next();
} else {
return res.sendStatus(401);
}
} else {
return res.sendStatus(401);
}
}
})
.catch(err => {
return res.sendStatus(401);
});
} catch (error) {
return res.sendStatus(401);
}
}
You are combining async/await with then/catch in your checkRole middlware, so probably both your checkRole middleware and your endpoint handler try to send back the response.
Refactor your checkRole middleware like this:
const checkRole = async function CheckRoleAuth(req, res, next) {
try {
const token = req.headers.authorization.split(' ')[1];
const decodedToken = jwt.verify(token, envs.jwtSecret);
const user = await User.findById(decodedToken.userId).lean();
if (!user) return res.sendStatus(401);
if (!user?.role?.includes('Admin')) return res.sendStatus(403);
req.userData = { email: decodedToken.email, id: decodedToken.id };
next();
} catch (error) {
return res.sendStatus(401);
}
};

Getting CORS Error for Invalid POST, PATCH - REST Endpoints

I'm working on Express.js and sending data in JSON. I'm getting the CORS Error (below) when passing POST, PATCH requests on Invalid Endpoints. For valid POST, PATCH Endpoints, there is no CORS error.
Also, for invalid/valid GET endpoint requests, there's no CORS error though.
Error Message:
Access to fetch at 'http://localhost:3000/invalid-endpoint' from origin 'https://www.google.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
Fetch Command I'm sending for Invalid POST Endpoint:
fetch('http://localhost:3000/invalid-endpoint', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
someValue: "999",
})
}).then(response => {
return response.json()}).then(data => {
console.log(data)}).catch(err => {
console.log(err)});
Headers I've included in my application:
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
next();
});
Complete Code:
//require modules.
//Headers
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
next();
});
//Endpoint Routes
app.use("/state", stateRoutes);
app.use("/district", districtRoutes);
app.use('/admin', adminRoutes);
app.use("/", stateDistrictRoutes); //also Home
/* All the invalid endpoints should fall into this. But CORS Error preventing that for POST and PATCH. GET is working fine here. */
app.use((req, res, next) => {
const error = new Error("Invalid Endpoint.")
error.status = 404;
throw error;
});
//All the errors should fall into this at last.
app.use((error, req, res, next) => {
const status = error.status || 500;
const message = error.message;
res.status(status).json({
status: status,
message: message,
info: error.data || null,
});
});
//connection to database....etc.
P.s.:- adminRoutes.js page (/admin). Below POST/PATCH requests work perfectly.
const express = require("express");
const router = express.Router();
const adminControllers = require("../controllers/adminController");
const adminAuthControllers = require("../controllers/adminAuthController");
router.post("/add/state", adminControllers.postState);
router.post("/add/district", adminControllers.postDistrict);
router.patch("/update/state/:state", adminControllers.patchState);
router.patch("/update/:state/:district", adminControllers.patchDistrict);
router.post("/login", adminAuthControllers.postLogin);
router.post("/signup", adminAuthControllers.postSignup);
module.exports = router;
adminControllers Page
const State = require("../models/state");
const District = require("../models/district");
exports.postState = (req, res, next) => {
const body = req.body;
State.findOne({ name: req.body.name })
.then((data) => {
if (data) {
const error = new Error("State already Exists!");
error.status = 409;
throw error;
}
const state = new State({
...body,
creator: {
lastUpdatedBy: req.userEmail,
createdBy: req.userEmail,
},
});
return state.save();
})
.then((response) => {
res.status(201).json({
message: "State created Successfully!",
data: response,
});
})
.catch((err) => {
if (!err.status) {
err.status = 500;
}
next(err);
});
};
exports.postDistrict = (req, res, next) => {
const body = req.body;
District.findOne({
name: req.body.name,
state: req.body.state,
})
.then((data) => {
if (data) {
const error = new Error("District already Exists!");
error.status = 409;
throw error;
}
const district = new District({
...body,
creator: {
lastUpdatedBy: req.userEmail,
createdBy: req.userEmail,
},
});
return district.save();
})
.then((response) => {
res.status(201).json({
message: "District created Successfully!",
data: response,
});
})
.catch((err) => {
if (!err.status) {
err.status = 500;
}
next(err);
});
};
exports.patchState = (req, res, next) => {
const state = req.params.state;
const body = req.body;
State.findOneAndUpdate(
{ name: state },
{ ...body },
{
new: true,
}
)
.then((response) => {
res.status(200).json({
message: "State updated Successfully!",
data: response,
});
})
.catch((err) => {
if (!err.status) {
err.status = 500;
}
next(err);
});
};
exports.patchDistrict = (req, res, next) => {
const state = req.params.state;
const district = req.params.district;
const body = req.body;
District.findOneAndUpdate(
{ name: district, state: state },
{ ...body },
{
new: true,
}
)
.then((response) => {
res.status(200).json({
message: "District updated Successfully!",
data: response,
});
})
.catch((err) => {
if (!err.status) {
err.status = 500;
}
next(err);
});
};
Thanks!
Stay Safe.
Response to preflight request doesn't pass access control check: It
does not have HTTP ok status.
This error seems to indicate a preflight request failed because its http status was not in the 200 range. Note such preflight requests come into the picture only for CORS requests that are considered complex - such as POST or PATCH requests.
Your server is not set up to specifically handle those types of preflight requests that I can see - so these actually end up being caught by your 404 route handler and hence the error message you're seeing. What is not clear to me is why your valid POST and PATCH endpoints do not error out in a similar fashion.
In order to avoid such errors, you should set up your server to handle preflight requests for all endpoints - including invalid ones. Something along the lines of the following should do the trick:
app.options("*", function(req, res) {
res.status(204).send()
)
Note this should come before your catch-all 404 middleware - and after the CORS middleware.
Edit: based on the below test program, it actually would appear that express automatically adds an OPTIONS route handler at the end of the middleware stack for paths to which a route handler has otherwise been explicitly attached.
var express = require("express");
var logger = require('morgan');
var app = express()
app.use(logger('dev'));
app.get("/exist", function (req, res) {
res.send("ok")
})
app.options("/intercept", function (req, res) {
res.sendStatus(204)
})
app.listen(3000, () => console.log("Server listening"))
// console output
Server listening
OPTIONS /exist 200 2.972 ms - 8
OPTIONS /nonexistant 404 1.865 ms - 154
OPTIONS /intercept 204 0.597 ms - 11
As a sidenote, express also seems to add an implicit 404 handler, although this may not be what you want - the content type in particular is html.

What's the issue which fails me to download data from mongodb using GridFS?

I am trying to write an application which functionality includes storing files in MongoDB. I succeeded in uploading files there, with the GridFS library, but then I failed many times trying to get access to the data. I constantly get a response with internal server error while loading files metadata with this request:
router.get('/uploads', async (req,res) => {
try {
gfs.files.find().toArray( (err, files) => {
if(!files || files.length === 0) {
return res.status(404).json({message: 'No files exisits'})
}
})
res.status(200).json(files);
} catch (error) {
return res.status(500).json({ message: "Could not find files, please try again" });
}})
content-type: application/json; charset=utf-8
The other request for downloading a certain file data ruins my whole backend and I get this error:
Proxy error: Could not proxy request /api/user/getuser from localhost:3000 to http://localhost:4000/ (ECONNREFUSED).
After that none of my requests work properly on any page.
And that's a nodejs code of that request:
router.get('/uploads/:filename', async (req,res) => {
try {
gfs.files.findOne({filename: req.params.filename}, (err, file) => {
if(!file || file.length === 0) {
return res.status(404).json({message: 'No file exisits'})
}
})
res.status(200).json(file);
} catch (error) {
return res.status(500).json({ message: "Could not find files, please try again" });
}})
GridFs configurations are next:
const conn = mongoose.createConnection(config.get('mongoURI'));
conn.once('open', () => {
gfs = Grid(conn.db, mongoose.mongo);
gfs.collection('uploads');
})
const storage = new GridFsStorage({
url: config.get('mongoURI'),
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(15, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString('hex') + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: 'uploads'
};
resolve(fileInfo);
});
});
}
});
I assume that I missed something important in documentation, and I'm not even trying to download a whole image, I am stuck. Useful advices would be highly appreciated!
I found the issue which caused an error! While emulating my request I got this error:
[0] Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
Therefore the problem was in that part of my code:
res.status(200).json(files);
Nodejs does not allow to set the header of status after the actual body of request was sent.
So all the fixes I had to do were:
res.status(200).json(files) to --> res.json(files);
I hope the solution would be useful for someone.

CORS policy error calling Cloud Function HTTP trigger

I wrote and deployed a Firebase Cloud function with CORS support:
const cors = require('cors')({
origin: true
});
...
exports.test = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const idToken = req.query.idToken;
admin.auth().verifyIdToken(idToken)
.then((decoded) => {
var uid = decoded.uid;
return res.status(200).send(uid);
})
.catch((err) => res.status(401).send(err));
});
});
I call the HTTP Trigger from my React app using the Axios package:
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
// Send token to your backend via HTTPS
// ...
console.log(idToken);
axios.get('https://XXX.cloudfunctions.net/test?idToken='+idToken)
.then(response => console.log(response));
}).catch(function(error) {
// Handle error
});
Unfortunately, when running the app on my local server, I still get an error:
Failed to load...Redirect From..to..
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.
PS. I have used the procedure reported here
I found an answer here
exports.test = functions.https.onRequest((req, res) => {
cors(req, res, () => {});
const idToken = req.query.idToken;
admin.auth().verifyIdToken(idToken)
.then((decoded) => {
var uid = decoded.uid;
return res.status(200).send(uid);
})
.catch((err) => res.status(403).send(err));
});

Firebase Functions + CORS

Having an issue with a firebase function that I need to work with cors. Based off the documentation and all the posts I've read it should be working but seem's like no matter what I try I keep getting the same error:
Failed to load <URL>: 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 500.
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
And here is the corresponding code in my firebase functions index.js file:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const cors = require('cors')({origin: true});
const stripe = require('stripe')('<TEST_KEY>');
const gcs = require('#google-cloud/storage')({keyFilename: '<PATH_TO_KEY>'});
const Easypost = require('#easypost/api');
const api = new Easypost('<TEST_KEY>');
admin.initializeApp(functions.config().firebase);
exports.processOrder = functions.https.onRequest((req, res) => {
cors(req, res, () => {
var body = JSON.parse(req.body);
if (
!body.shipment_id ||
!body.items ||
!body.card
) return res.set('Access-Control-Allow-Origin', '*').send({error: true, message: 'Missing information'});
getPrices(body.items, (err, prices, totalPrice) => {
if (err) return res.set('Access-Control-Allow-Origin', '*').send({error: err, message: "Error"})
// Create a new customer and then a new charge for that customer:
stripe.customers.create({
email: 'test#example.com'
}).then((customer) => {
return stripe.customers.createSource(customer.id, {
source: body.card.token.id
});
}).then((source) => {
return stripe.charges.create({
amount: (totalPrice * 100),
currency: 'usd',
customer: source.customer
});
}).then((charge) => {
return res.set('Access-Control-Allow-Origin', '*').send({error: false, message: "Success"});
}).catch((err) => {
console.log(err);
return res.set('Access-Control-Allow-Origin', '*').send({error: err, message: "Error"});
});
});
});
});
Any help would be greatly appreciated :)
Edit: Just wanted to note: I've tried only setting res.set('Access-Control-Allow-Origin', '*') and not using the cors middleware, and I've tried not setting the header and only using cors. Neither of which worked :(
Solution: As #sideshowbarker said in a comment, my function had an error elsewhere before returning. The Access-Control-Allow-Origin was never even getting set. Once I fixed the error it was all good! Ty!
In node you can use a package to solve this problem. To enable all CORS install the following package:
npm install cors
Then assuming you are using express you can then enable CORS by the following lines of code:
var cors = require('cors');
app.use(cors());

Categories

Resources