CORS block in firebase cloud function - node.js

I'm implementing a cloud function to get results from BigQuery. My functions/index.js code below
const functions = require('firebase-functions');
const {BigQuery} = require('#google-cloud/bigquery');
const cors = require('cors')({origin: true});
exports.getBigQueryData = functions.region('europe-west3').https.onRequest((req,res) => {
cors(req,res,() => {
const bigquery = new BigQuery({
projectId: 'neon-opus-585',
keyFilename: 'service_account_bq.json'
});
const query = "SELECT * FROM `xxxx`";
bigquery.createQueryJob({query: query}).then((data) => {
const job = data[0];
return job.getQueryResults();
})
.then(results => {return res.send(results)})
.catch(error => {console.log(error)})
})
})
When i only deploy / test the function, everything runs OK, but when I use it from inside my application I get the following error
Access to fetch at 'https://xxxx.cloudfunctions.net/xxxx' from origin 'http://localhost:5000' 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. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
As you can see I tried importing the cors package and implement it in my function as suggesting by several posts here but it is still not working.
Any help would be appreciated :)

Take a look into Handling CORS requests, there you have an example about this:
To handle a preflight request, you must set the appropriate Access-Control-Allow-* headers to match the requests you want to accept:
exports.corsEnabledFunction = (req, res) => {
// Set CORS headers for preflight requests
// Allows GETs from any origin with the Content-Type header
// and caches preflight response for 3600s
res.set('Access-Control-Allow-Origin', '*');
if (req.method === 'OPTIONS') {
// Send response to OPTIONS requests
res.set('Access-Control-Allow-Methods', 'GET');
res.set('Access-Control-Allow-Headers', 'Content-Type');
res.set('Access-Control-Max-Age', '3600');
res.status(204).send('');
} else {
res.send('Hello World!');
}
};
And Authentication with CORS:
If you plan to send a request with an Authorization header, you must:
Add the Authorization header to Access-Control-Allow-Headers.
Set the Access-Control-Allow-Credentials header to true.
Set a specific origin in Access-Control-Allow-Origin (wildcards are not accepted).
exports.corsEnabledFunctionAuth = (req, res) => {
// Set CORS headers for preflight requests
// Allows GETs from origin https://somedomain.com with Authorization header
res.set('Access-Control-Allow-Origin', 'https://somedomain.com');
res.set('Access-Control-Allow-Credentials', 'true');
if (req.method === 'OPTIONS') {
// Send response to OPTIONS requests
res.set('Access-Control-Allow-Methods', 'GET');
res.set('Access-Control-Allow-Headers', 'Authorization');
res.set('Access-Control-Max-Age', '3600');
res.status(204).send('');
} else {
res.send('Hello World!');
}
};

Related

CORS blocking axios request with 'Authorization' Header and Data

Trying to send an axios post request from a Vue app (localhost) to my nodejs API (both localhost and heroku).
There are no issues receiving the response if the request is sent without data or headers, but as soon as I add them I get the following error:
Access to XMLHttpRequest at 'https://myapp.herokuapp.com/myendpoint' 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.
I have tried different options, both server and client side, as suggested on similar questions but had no success.
Client Request:
const apiUrl = 'https://myapp.herokuapp.com/myendpoint'
//const apiUrl = 'http://localhost:5000/myendpoint'
const token = Buffer.from(`${this.userid}:${this.password}`).toString('base64')
const data = {
'mydata': 'some data'
}
axios.post(apiUrl, data, {
headers: {
Authorization: "Basic " + token
}
}).then( res => {
console.log(res)
}).catch( err => {
console.log(err)
})
Server Endpoint:
app.post("/myendpoint", (req, res) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.send('This is the API\'s response')
})
Some of the answers I tried:
Response to preflight request doesn't pass access control check
Nodejs Express CORS issue with 'Access-Control-Allow-Origin'
https://www.moesif.com/blog/technical/cors/Authoritative-Guide-to-CORS-Cross-Origin-Resource-Sharing-for-REST-APIs/
CORS authorization issue while using axios
How to send authorization header with axios
I think it is better if you define your cors using a global middleware. First off, install cors by using npm i cors.
Then, I'll show an example of how that package could be used.
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
// your routes and things here...
Then, ensure that your front-end also uses withCredentials set to true in the axios request. This is done to ensure that the header is being sent properly.
axios.post(apiUrl, data, {
headers: {
Authorization: "Basic " + token
},
withCredentials: true,
}).then(() => ...);
Sometimes, if you define Access-Control-* manually, you might forget something. That's why I recommend you to use cors.

The same node.js backend service, behaves different for the same request from different clients

There is a backend service which is resides on azure portal and developed with node.js by using Express.js
Requests are sending from an azure devops extension.
Most of the user's requests are successful but just a few users stuck on cors errors.
The cors error is
Access to fetch at ..... from origin .... has been blocked by CORS
policy: No 'Access-Control-Allow-Origin' header is present on the
request resource. If an opaque response serves your needs, set the
request's mode to 'no-cors' to fetch the resource wiht CORS disabled.
A part of node.js server code (probably there are unnecessary parts in it)
var cors = require('cors')
app.use(cors())
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
res.setHeader('Access-Control-Allow-Headers', '*');
res.setHeader("Content-Type", "application/json");
res.setHeader("Content-Type", "text/html");
next();
});
Successful request headers
Failed request headers
A get method example from client code
export function getTheValue() {
return fetch('https://....../.....')
.then(response => {
return response.text();
})
.catch(error => {
return reject(error);
})
}

How to set Access-Control-Allow-Headers header in node-http-proxy

I am using the coinbase-pro library to make post request to the coinbase sandbox api through a form on localhost. I am trying to use node-http-proxy to get around a CORS error with no success. Ive been banging my head against the wall for a while on this, any help would be appreciated.
const express = require("express");
const httpProxy = require("http-proxy");
const port = 5050;
const app = express();
const proxy = httpProxy.createProxyServer({});
app.use(function(req, res) {
delete req.headers["origin"];
delete req.headers["referer"];
delete req.headers["host"];
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader(
"Access-Control-Allow-Headers",
"Content-type, cb-access-key,cb-access-passphrase,cb-access-sign,cb-access-timestamp"
);
res.setHeader(
"Access-Control-Allow-Methods",
"GET,POST,PUT,DELETE,OPTIONS"
);
const apiURL = 'https://api-public.sandbox.pro.coinbase.com'
proxy.web(req, res, { target: apiURL });
});
app.listen(port, () =>
console.log("Started proxy on port", port)
);
error:
Access to fetch at 'http://localhost:5050/orders' from origin 'http://localhost:3000' has been blocked by CORS policy: Request header field cb-access-passphrase is not allowed by Access-Control-Allow-Headers in preflight response.
The answer is here:
I think modifying the proxy response header is not covered in the current doc.
proxy.on('proxyRes', function(proxyRes, req, res) {
console.log('Raw [target] response', JSON.stringify(proxyRes.headers, true, 2));
proxyRes.headers['x-reverse-proxy'] = "custom-proxy";
proxyRes.headers['cache-control'] = "max-age=10000";
console.log('Updated [proxied] response', JSON.stringify(proxyRes.headers, true, 2));
// Do not use res.setHeader as they won't override headers that are already defined in proxyRes
// res.setHeader('cache-control', 'max-age=10000');
// res.setHeader('x-reverse-proxy', 'custom-proxy');
});
The key is to use proxyRes inside "proxyRes" event like proxyRes.headers[key] = value instead of relying on res.setHeader(key, value) as res.setHeader does not work when key is already exists among the proxy target response headers.

The "x-auth-token" header in Router.all() returns undefined

I am currently building an application using React for frontend and Nodejs for backend powered by Express.js.
I'm using jsonwebtoken for security method and applying a middleware called auth.js to authorize the request on every endpoints that starts with /rest, here is the code for auth.js:
const token = req.header('x-auth-token');
console.log(token); // Get the token from 'x-auth-token' header
if (!token) {
return res.status(400).json({ msg: 'Authorization denied. ' });
}
try {
// validate the token
next();
} catch (e) {
return res.status(401).json({ msg: 'Invalid token. '})
}
and the routing for /rest/* endpoints:
router.all("/", auth, (req, res) => {
// some codes
});
the request:
fetch(url + "/rest", {
method: "GET",
mode: "cors",
headers: {
"x-auth-token" : "this is the token" // define the header
"Accept" : "application/json",
"Content-Type" : "application/json",
}
});
The router.all() mechanism works fine, I'm able to access every /res routes with all methods. The problem is, the value of the x-auth-token header in the auth.js middleware always gives "undefined". when I change the routing to route.get() or route.post() etc.., that value of the x-auth-token returns the token from client correctly.
Am I missing something with the work around this router.all()? Thank you all.
EDIT: here's my cors middleware
module.exports = cors = (req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', {domain});
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type , Accept, x-auth-token');
res.setHeader('Access-Control-Allow-Credentials', true);
next();
}
SOLVED:
So turns out, the reason why my x-auth-token header is missing in the req is because of the Pre-flight request mentioned by #Marcos Casagrande.
Now, what I went with is installing the CORS package and configured it following the Express documents and ended up with the following snippet in the server.js file since I want that cors configuration to be applied on every endpoints:
let cors = require("cors");
let whitelist = [{domains}]
let corsOptions = {
origin: (origin, callback) => {
if (whitelist.indexOf(origin) !== -1 || !origin) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
}
app.use(cors(corsOptions));
Thank you all for helping me out.
When using router.all, OPTIONS will need to be handled, and x-auth-token won't be available there.
When you issue a request from the browser, an OPTIONS request will be issued first by the browser.
If you put:
console.log(req.method, req.headers);
You'll see: OPTIONS & x-auth-token missing. After OPTIONS has been handled correctly, the browser will issue the GET request, where the header will be present.
So you can handle it your self, and set the right Access-Control-Allow-Origin header if issuing a CORS request, or use cors package.
const app = require('express')();
const cors = require('cors');
app.use(cors());
// ...
router.all("/", auth, (req, res) => {
// No OPTIONS here, was already handled by `cors`
});
If you're not issuing a CORS request just use this in your auth middleware:
if(req.method === 'OPTIONS')
return res.send(); // 200
or handle options first
router.options('*', (req, res) => res.send());
router.all("/", auth, (req, res) => {
// some codes
});
Read more about Preflight Request
I see you are trying to access header value from req.header and not from req.headers, and you have a "Content-Type" : "application/json", in you GET request which will make a OPTION request anyway.
Your client app is making a cross origin request and you nodejs server must handle it. You can use the cors to solve this.
You can send token in any custom header, but a better practice/standardisation is to use Authorization header.

Why does my Google Cloud Function kick up CORS policy issues when I add a console log with a reference to variable?

I have a basic node.js cloud function. It accepts a request and just returns the body of the message; it's not working correctly and I'm trying to debug with console logs.
/**
* Responds to any HTTP request.
*
* #param {!express:Request} req HTTP request context.
* #param {!express:Response} res HTTP response context.
*/
exports.helloWorld = (req, res) => {
res.set('Access-Control-Allow-Origin', '*');
console.log("req: ");
if (req.method === 'OPTIONS') {
// Send response to OPTIONS requests
res.set('Access-Control-Allow-Methods', 'GET');
res.set('Access-Control-Allow-Headers', 'Content-Type');
res.set('Access-Control-Max-Age', '3600');
res.status(204).send('');
} else {
let message = req.query.message || req.body.message || 'Hello World!';
let rets ={};
rets['message'] = message;
res.status(200).send(JSON.stringify(rets));
}
};
The above works fine. But if I change the console log line to this:
console.log("req: " + JSON.stringify(req) );
I get the following error:
Access to fetch at 'https://us-central1-delta-xxxxx-1111111.cloudfunctions.net/getEvents' 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.
I just can't figure out why the console statement would affect the CORS stuff. I added the checks in the above code to try and fix these CORS issues
EDIT 1:
I notice that I don't get the CORS error if I instead console log the body of the request? The below works fine
console.log(JSON.stringify(req.body) );
Try to turn on CORS mode on the client side.

Resources