Node Express Cors issue - node.js

I cant figure why the cors express middleware wont work. cors, express, and ejs are all saved in package.json. The app works fine if I add corsanywhere proxy on the front end but id like to work around this on the server side. any help much appreciated I've been stuck on this.
the api is in the get View/index path
the error is:
Access to fetch at 'https://api.darksky.net/forecast/' 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.
const express = require('express');
const app = express();
const ejs = require('ejs');
const cors = require('cors');
const PORT = process.env.PORT || 3000;
// app.use((req, res, next) => {
// res.header('Access-Control-Allow-Origin', '*')
// res.header('Access-Control-Allow-Headers', 'Origin', 'X-Requested-With')
// next();
// });
app.use(cors());
app.use(express.static(__dirname + '/Public'));
app.set('view engine', 'ejs');
app.get('/', cors(), (req, res) => {
res.render(__dirname + '/Views/index')
});
app.listen(PORT, () => {
console.log(`server is listening on ${PORT}`)
});
client side:
it works with the ${proxy} in there but id like to get rid of that
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(position => {
long = position.coords.longitude;
lat = position.coords.latitude;
var proxy = 'https://cors-anywhere.herokuapp.com/'
var api = `${proxy}https://api.darksky.net/forecast/042750f3abefefdfe2c9d43cf33ce576/${lat},${long}`;
fetch(api)
.then(response => {
return response.json();
})
.then(data => {
let {temperature, summary, icon,} = data.currently;
temperatureDegree.textContent = Math.floor(temperature);
temperatureDescription.textContent = summary;
locationTimezone.textContent = data.timezone;
setIcons(icon, document.querySelector('.icon'
w
``````

So, if you're trying to access some other service https://api.darksky.net/forecast/ (that you don't control) from your web page, then there is nothing you can do to make CORs work for that. It's up to the api.darksky.net server to decide if CORs is allowed or not. You can't change that.
You could make a request from your web page to your server to ask it to get some data from api.darksky.net for you and then return it back to your webpage (working as a simple proxy). Your server is not subject to any CORs limitations when accessing api.darksky.net. Only browsers are limited by CORs.
And, as you've found, you can also use a proxy service that enables CORs and fetches data for you.
Let's suppose you want to proxy the parts of the darksky API, you could do something simple like this:
const express = require('express');
const app = express();
const request = require('request');
const apiRouter = express.Router();
// maps /api/forecast/whatever to http://api.darksky.net/forecast/developerKey/whatever
// and pipes the response back
const apiKey = "yourAPIKeyHere";
apiRouter.get("/*", (req, res, next) => {
// parse out action and params
// from an incoming URL of /api/forecast/42.3601,-71.0589
// the /api will be the root of the router (so not in the URL here)
// "forecast" will be the action
// "42.3601,-71.0589" will be the params
let parts = req.path.slice(1).split("/"); // split into path segments, skipping leading /
let action = parts[0]; // take first path segment as the action
let params = parts.slice(1).join("/"); // take everything else for params
request({
uri: `https://api.darksky.net/${action}/${apiKey}/${params}`,
method: "get"
}).pipe(res);
});
app.use("/api", apiRouter);
app.listen(80);
Now, when you send this server, this request:
/api/forecast/42.3601,-71.0589
it will request:
https://api.darksky.net/forecast/yourAPIKeyHere/42.3601,-71.0589
and pipe the result back to the caller. I ran this test app and it worked for me. While I didn't see anything other than forecast URLs in the darksky.net API, it would work for anything of the format /api/someAction/someParams.
Note, you probably do NOT want to enable CORS on your server because you don't want other people's web pages to be able to use your proxy. And, since you're just sending requests to your own server now, you don't need CORS to be able to do that.

Related

axios get request to express server is only successful for the first time

I'm trying to setup a route for downlading videos for my Vue app backed by an Express server. For some reason, first request that is sent to backend is working as expected and it results in successful file download; however, the subsequent requests fail with Network Error, and I only get a brief error message that looks like this http://localhost:8080/download/videos/1667163624289.mp4 net::ERR_FAILED 200 (OK).
What could be the issue here?
I have an Express.js server (localhost:8000) setup with cors like below:
const express = require("express");
const app = express();
const port = 8000;
const cors = require("cors");
app.use(cors());
app.get("/download/:kind/:fileName",
async (req, res, next) => {
const file = `${__dirname}/public/files/${req.params.kind}/${req.params.fileName}`;
res.download(file);
});
app.listen(port, () => {
});
And my Vue (localhost:8080) component sends that request looks like this:
downloadVideo(fileName) {
const fileName = fileDir.split('/').pop();
const downloadUrl = `/download/videos/${fileName}`;
axios({
method: "get",
url: downloadUrl,
responseType: 'blob',
})
.then((response)=> {
// create file link in browser's memory
const href = URL.createObjectURL(response.data); // data is already a blob
// create "a" element with href to file & click
const link = document.createElement('a');
link.href = href;
link.setAttribute('download', 'my_video.mp4');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(href);
})
.catch((err) => {
// HANDLE ERROR HERE
})
},
I also have a vue config setup to proxy the requests to 8000:
// vue.config.js
module.exports = {
devServer: {
proxy: 'http://localhost:8000',
disableHostCheck: true
},
outputDir: '../backend/public', // build will output to this folder
assetsDir: '' // relative to the output folder
}
Instead of manually setting up the route for downloading files, you can directly set the static files directory as a controller and remove the app.get controller for downloading.
app.use(express.static("public/files"))
Then, on the client side, instead of downloading the file using JS, converting it into a data url, and then downloading it, you can do the following:
downloadVideo(fileName) {
// whatever you want to do to the file name
const parsedFileName = fileName
const link = document.createElement('a');
link.href = parsedFileName;
link.setAttribute('download', 'my-video.mp4'); // or pdf
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
Here is a sample working example
Im not really sure why the first request is going through. But the error looks like a CORS problem.
Basically, your frontend and backend run on different ports, which are treated like a different server altogether by the CORS checks.
I took the following config from the cors package docs
var express = require('express')
var cors = require('cors')
var app = express()
var corsOptions = {
origin: 'http://localhost:8080',
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
/*
Or use this, if you want to bypass cors checks - not a safe practice
var corsOptions = {
origin: '*',
optionsSuccessStatus: 200
}
*/
app.get('/products/:id', cors(corsOptions), function (req, res, next) {
res.json({msg: 'This is CORS-enabled for only localhost:8080'})
})
app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})
The 200 ERR is funny, because normally 200 means OK. But in this case 200 refers to the Preflight request and ERR to the fact that the Ports are different.
There is a good video about cors on youtube

Why do I get a CORS Error when tunneling through ngrok?

I know this kind of problem has been solved before, but I can't figure out exactly why it is not working for my case.
I am working on a website locally and I want to test it out on various platforms and devices so I decided to use ngrok for this.
My front-end is running on port 3000 and my express server on port 5000.
So I opened ngrok and entered ngrok http 3000
On my local PC, where the server is running, the https://example.ngrok.io is working as intended without any problems.
But on my laptop (or another device), the front-end displays correctly but when it is actually going to get data from the back-end, it is showing the error: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:5000/weather/51.87575912475586,0.9436600208282471. (Reason: CORS request did not succeed).
On my express server, I made sure to use the cors package and app.use(cors()); and I also tried adding the headers manually :
app.all('/*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
next();
});
Source: Why doesn't adding CORS headers to an OPTIONS route allow browsers to access my API?
Here is also my code where I am fetching and getting data in case I am doing something wrong there:
index.js (front-end)
const response = await fetch(`http://localhost:5000/weather/${lat},${lng}`); //sending request to server-side
const json = await response.json();
console.log(json); //getting the weather data from server-side
server.js (back-end)
const express = require("express");
const mongoose = require("mongoose");
const fetch = require("node-fetch");
const cors = require('cors');
const nodemailer = require('nodemailer');
require('dotenv').config();
const users = require('./routes/api/users');
const app = express();
//Json Middleware
app.use(express.json());
app.use(cors());
//Getting URI from keys file
const db = require('./config/keys').mongoURI;
//Connect to the Database
mongoose.set('useUnifiedTopology', true);
mongoose.set('useCreateIndex', true);
mongoose.connect(db, {useNewUrlParser: true})
.then(()=> console.log("Database Connected"))
.catch(err=> console.log(err));
//Route for user routes
app.use('/api/users',users);
const dbport = process.env.PORT || 5000;
app.listen(dbport, () => console.log(`Server started on port ${dbport}`));
app.get('/weather/:latlon', async (req,res) =>{ //awating request from client-side
const latlon = req.params.latlon.split(',');
console.log(req.params);
const lat = latlon[0];
const lon = latlon[1];
console.log(lat,lon);
const api_key = process.env.API_KEY;
const weather_url = `https://api.darksky.net/forecast/${api_key}/${lat},${lon}?units=auto`; //getting data from weather API
const fetch_res = await fetch(weather_url);
const json = await fetch_res.json();
res.json(json); //sending weather data back to client-side
});
Is this possible to work or not due to the nature of localhost?
Both firefox and chrome had the same problem.
Thanks for the help!
After some days of head scratching, I finally found a solution and I'm posting it below for others that may have the same problem.
Step 1:
Instead of having 2 ports active (3000 for client and 5000 for server), I closed my client port and served my client folder/assets directly from my server using express:
const dbport = process.env.PORT || 5000;
app.listen(dbport, () => console.log(`Server started on port ${dbport}`));
app.use(express.static('client')); //serving client side from express
//Json Middleware
app.use(express.json());
Step 2:
Now that we have one port (port 5000) for both the client and the server, I went into my client side where I did my fetch requests (see above at index.js) and modified the actual requests to be relative:
const response = await fetch(`/weather/${lat},${lng}`); //sending request to server-side
const json = await response.json();
console.log(json); //getting the weather data from server-side
Step 3:
Finally, I opened ngrok and typed:
ngrok http 5000
It should now work.
If you are using ngrok with nodejs/express.js .
Remove he cors import and use this code:
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "YOUR-DOMAIN.TLD"); // update to match
the domain you will make the request from
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-
Type, Accept");
next();
});
replace "YOUR-DOMAIN.TLD" with "*" to give access to all urls OR your specific website url.
Refer to https://enable-cors.org/server_expressjs.html for more details
Thank You.

Firefox CORS failure with express node server

I'm using Express.js with the CORS middleware. I'm getting very strange behavior on Firefox v73.0.1 (64-bit)
Clean firefox profile... so nothing is cached.
I try a direct request to http://localhost/search?q=AMZN, I get results as expected.
I open my webapp which is running on localhost:3000. All of these requests are not using TLS/SSL.
I get "Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost/search?q=AMZN. (Reason: CORS request did not succeed)." when it tries to reach out to the API server backend.
I refresh the other tab where I was accessing localhost directly (same request which succeeded before), and I get a "connection reset".
Chrome does not do this...
I have been looking at wireshark and Firefox simply sends a GET request (several of them? Why?? I'm only making one), sends the headers and such, then it is followed by connection reset.
Sounds like node is doing something wrong? Or I dunno.
const express = require('express');
const fetch = require('node-fetch');
const util = require('util');
const app = express();
const port = 80;
var cors = require('cors')
var copts = {
origin: function (origin, callback) {
console.log('yeah');
// allow all
callback(null, true);
}
}
const SEARCH_URL = 'https://api.stocktwits.com/api/2/streams/symbol/%s.json';
app.get('/search', cors(copts), (req, res) => {
The "yeah" part never is hit at all.
Very simple clientside code.
return fetch(BACKEND_URL + uri).then(response => {
Tried removing CORS middleware and added these headers, based on examining github's cross-browser request-response in chrome
res.header('Access-Control-Allow-Methods', 'GET');
res.header('Access-Control-Max-Age', '3600');
res.header('Vary', 'Origin, Access-Control-Request-Headers, Access-Control-Request-Method, Accept-Encoding');
res.header('Access-Control-Allow-Origin', '*');
I didn't really have the options handler captured so maybe this didnt matter. I found those headers in the GET response.
It might be possible that the node server get crashed on an invalid reqeust and hence you are getting the reset error.
With cors package:
You need to add optionsSuccessStatus property in the cors options for some legacy browsers. And OPTIONS handler To enable pre-flightin requests.
const express = require('express');
const fetch = require('node-fetch');
const util = require('util');
const app = express();
const port = 80;
var cors = require('cors')
var copts = {
origin: function (origin, callback) {
console.log('yeah');
callback(null, true);
},
optionsSuccessStatus: 200
}
app.options("*", cors(copts));
const SEARCH_URL = 'https://api.stocktwits.com/api/2/streams/symbol/%s.json';
app.get('/search', cors(copts), (req, res) => { });
With headers.:
You are missing Access-Control-Allow-Headers in the response headers, you need to add the Access-Control-Allow-Headers header in the response header to allow the Access-Control-Allow-Origin header.
Try this.
const express = require('express');
const fetch = require('node-fetch');
const util = require('util');
const app = express();
const port = 80;
var cors = require('cors')
var copts = {
origin: function (origin, callback) {
console.log('yeah');
// allow all
callback(null, true);
}
}
const SEARCH_URL = 'https://api.stocktwits.com/api/2/streams/symbol/%s.json';
app.get('/search', (req, res) => {
res.header('Access-Control-Allow-Methods', 'GET, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Origin');
res.header('Access-Control-Allow-Origin', '*');
res.status(200).send("Ok")
});
in my case, FireFox removes the request header from ajax to a http url, but works fine with https
Have you updated your front end package.json file with the following?
"proxy": "http://localhost:[your port here]/",
try assigning CORS to the express server like so:
const cors = require('cors');
app.use(cors());

Why and how this simple node.js proxy works?

I have a frontend-only web application on Netlify which has to consume an API on OpenSubtitles.org. Although OpenSubtitles.org enables CORS, sometimes I got preflight errors, so I decided to use a proxy.
I had problems using Netlify's proxy feature, so I decided I will create my own proxy on Heroku, and send my requests from the frontend to there, so these will be proxied to OpenSubtitles.org from a server.
I came up with the following based on the code I found here:
const express = require('express');
const request = require('request');
express()
.use('/', function(req, res) {
req.pipe(
request({
url: 'http://rest.opensubtitles.org/search' + req.url,
headers: {
'User-Agent': 'TemporaryUserAgent'
}
})
).pipe(res);
})
.listen(process.env.PORT || 8000);
I thought I deploy this, try it out, then I will enable CORS on it after that. However I've just realized it is working perfectly without doing anything else. How is it possible? Why can I call this from a frontend-only app on a different domain without explicitly enabling CORS?
Also, what if the server crashes, how to handle the errors there?
CORS is working because the url you're requesting responds with the header Access-Control-Allow-Origin set with a value of *. Since you're piping that response and its headers back to the original res object, it will enable CORS as if it was coming from your local proxy.
Below is a more straightforward example of how to proxy a request to another site and return its response intact using node streams.
const express = require('express')
const request = require('request')
const port = process.env.PORT || 1337
let server = express()
const proxyMiddleware = (req, res, next) => {
let url = `https://www.google.com/${req.url}`
let proxyRequest = request(url)
// Pass request to proxied request url
req.pipe(proxyRequest)
// Respond to the original request with the response from proxyRequest
proxyRequest.pipe(res)
}
server.use(proxyMiddleware)
server.listen(port, () => console.log(`Listening on ${port}`))

Socket.io - Origin is not allowed access

I'm having this weird problem with socket.io. I have an express app which I run on port 5000. I have configured socket.io like this:
const app = require('../index');
const http = require('http');
const server = http.Server(app);
const io = require('socket.io')(server);
io.on('connection', function (socket) {
console.log('User has connected');
socket.emit('connect', {
message: 'Hello World'
});
});
Then I import this piece of code into my index.js file like this:
const express = require('express');
const app = module.exports = express();
const bodyParser = require('body-parser');
const cors = require('cors');
const request = require('request');
const boxRoutes = require('./routes/v1/boxRoutes');
const bidRoutes = require('./routes/v1/bidRoutes');
// use body parser so we can get info from POST and/or URL parameters
app.use(bodyParser.urlencoded({ limit: '10mb', extended: true }));
app.use(bodyParser.json({ limit: '10mb' }));
require('./services/usersClass');
// cors set up
app.use(cors());
app.use(function (req, res, next) {
console.log('Headers Middleware Called');
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'origin, x-requested-with, content-type, accept, x-xsrf-token', 'token');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
// Request headers you wish to expose
res.setHeader('Access-Control-Expose-Headers', false);
next();
});
// Middleware to authenticate the requests to this service
app.use(function(req, res, next) {
console.log('Auth Middleware Called');
if(!req || !req.headers['authorization']) return res.sendStatus(401);
const token = req.headers['authorization'].split(' ')[1];
request.post(
'http://localhost:4000/api/v1/users/auth',
{
headers: {
'Authorization': `Bearer ${token}`
}
},
function (error, response, body) {
if (!error && response.statusCode == 200) {
const data = JSON.parse(body);
res.locals.user = data.user;
next();
} else {
console.log('Request has failed. Please make sure you are logged in');
res.sendStatus(401);
}
}
);
});
app.use('/api/v1/boxes/', boxRoutes);
app.use('/api/v1/bids/', bidRoutes);
// disable 'powered by'
app.disable('x-powered-by');
app.listen(5000, () => {
console.log('Trading service is running on port 5000');
});
Now, in my client code, I try to establish socket.io connection when the user logs in. Everytime I try to connect to the server, I get the following error:
Failed to load
http://localhost:5000/socket.io/?EIO=3&transport=polling&t=MA_9wXE:
Response to preflight request doesn't pass access control check: The
value of the 'Access-Control-Allow-Origin' header in the response must
not be the wildcard '*' when the request's credentials mode is
'include'. Origin 'http://localhost:3000' is therefore not allowed
access. The credentials mode of requests initiated by the
XMLHttpRequest is controlled by the withCredentials attribute.
I don't understand why the connection fails. I have configured Access-Control-Allow-Origin to my client domain but it still fails.
You can use cors npm module. It will fix your problem.
var cors = require('cors')
var app = express()
app.use(cors({origin: '*'}))
start '*' means allow every origins. You can type spesific origin too.
I've seen this problem before, but never seen it manifested as a cross origin issue. You are creating two separate http servers. One you are making your express server and the other you are making your socket.io server. The code you show only actually starts the express server and you show no code that actually starts your socket.io server.
Here's where you create these two separate servers:
const server = http.Server(app); // creates the http server you use for socket.io
app.listen(5000, () => {...}); // creates the http server you use with Express
Inside of app.listen(), it creates it's own new server and starts it. Your other server is never started (at least per the code you show here).
When you probably want to do is to make your socket.io server use the same server as your express server and then you should be able to connect just fine without any CORs issues.
If you want to use app.listen(), it will return the server object that it created and you need to use that to initialize socket.io.
If you want to use the other server, then you need to share that with your express initialization code so it can use that one.

Resources