Configuring CORS npm package to whitelist some URLs - node.js

I have an API here https://api-ofilms.herokuapp.com which send datas to my client https://ofilms.herokuapp.com,
I want to disable CORS for all origin URLs except :
- http://localhost:3000 (URL of the client in development),
- https://ofilms.herokuapp.com (URL of the client in production),
Because for now, you can see the message on https://api-ofilms.herokuapp.com but I don't want people to access the API,
I tried this before all routes :
const cors = require("cors");
app.use(
cors({
origin: ["http://localhost:3000", "https://ofilms.herokuapp.com"],
credentials: true
})
);
But I can still see API messages...

You can try passing in the origin with a callback, like this
Configuring CORS w/ Dynamic Origin
var express = require('express')
var cors = require('cors')
var app = express()
var whitelist = ['http://example1.com', 'http://example2.com']
var corsOptions = {
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
}
app.get('/products/:id', cors(corsOptions), function (req, res, next) {
res.json({msg: 'This is CORS-enabled for a whitelisted domain.'})
})
app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})
Source

Related

Tried everything I find but I can't solve CORS issue with Express server

I'm trying to fetch data from my Express API but I'm keep getting this error.
Firefox Console Error
Chrome Console Error
I have tried everything I find on internet.
I tried using different browser, browser extensions to bypass CORS check.
I thought maybe the issue is related to localhost, so I deployed it, but the same issue persisted.
I tried mockup API with the same frontend, and it fetches data just fine.
I tried manually adding Access-Control-Allow-Origin header on server side but did not work.
I also tried CORS middleware for Express and again did not work.
I'm getting proper responses with Postman just fine, but not within a browser.
This is my code on client side:
async create(visit) {
this.setState({visits: [...this.state.visits, {...visit}]})
fetch('http://localhost:8000/create-visit', {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: `{"params": ${JSON.stringify(visit)}}`
})
.then(resolve => resolve.json())
.then(result => {console.log(result)})
}
I also tried GET request with but no difference.
And this is my server side code:
const express = require('express')
const visitRouter = require('./routers/visits')
const cors = require('cors')
const PORT = process.env.PORT;
const app = express();
var corsOptions = {
credentials: true,
origin: "*",
preflightContinue: true,
allowedHeaders: 'GET,PUT,POST,DELETE,OPTIONS'
}
var logger = function(req, res, next) {
console.log(res)
next()
}
app.use(logger)
app.use(express.json())
app.use(visitRouter)
// app.use((req, res, next) => {
// res.append('Access-Control-Allow-Origin', ['*'])
// res.append('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS')
// res.append('Access-Control-Allow-Headers', 'Content-Type')
// next()
// })
// app.use(cors())
app.use(cors(corsOptions))
app.listen(PORT, () => {
console.log(`Listening from port: ${PORT}`)
})
Router:
const express = require('express')
const router = new express.Router()
const visitModel = require('../models/visits')
router.post('/create-visit', async (req, res) => {
try {
const params = req.body.params
console.log(params)
const newVisit = await visitModel.createVisit(params)
res.status(201).send(newVisit)
} catch (err) {
res.status(500).send(err)
}
})

Heroku backend Node.js and Netlify frontend react app has been blocked by CORS policy:

Heroku backend Node.js and Netlify frontend react app has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I've seen a lot of posts on this, but I just can't seem to fix what's creating this error. Of course, I believe it has to do with CORS. But as you can see, I've added multiple versions of CORS middleware to allow this to work. Locally everything is fine. Production/live is where I get the issue:
Access to XMLHttpRequest at 'https://seb-youtube-api.herokuapp.com//videos?page=1&limit=50' from origin 'https://seb-youtube-api.netlify.app' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Here is my backend server with Node.js and Express.js
They make a simple call to a youtube API.
require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser')
const app = express();
const cors = require('cors')
const chalk = require('chalk');
const { google } = require('googleapis');
const youtube = google.youtube('v3'); // initialize the Youtube API library
// Middleware
app.use(cors());
app.use(bodyParser.json());
/******************** GET REQUEST TO VIDEOS *********************/
app.get('/videos', async (req, res) => {
const results = await fetchYoutubePlaylist();
res.json(results)
})
// /******************** POST REQUEST, USER SEARCH *********************/
app.post('/videos', async (req, res) => {
console.log('POST QUERY',req.body)
const query = req.body
res.body = await fetchYoutubeSearch(query)
console.log("RES POST", res.body)
res.json(res.body)
})
app.use('*', cors(), (req, res) => {
return res.status(404).json({ message: 'Not Found' });
});
// CORS
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type,multipart/form-data,Authorization');
res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE');
if (req.method === 'OPTIONS') {
return res.send(204);
}
next();
});
/******************** FIRST YOUTUBE API CALL *********************/
const fetchYoutubePlaylist = async () => {
try {
const {data} = await youtube.playlistItems.list({
key: process.env.YOUTUBE_API_TOKEN,
part: ['snippet'],
maxResults: 50,
playlistId: "UUBh8XcZST_JTHt-IZDxT_pQ"
})
console.log(data)
return data.items
} catch(err) {
console.log(chalk.red(err))
}
}
/******************** SECOND YOUTUBE API CALL *********************/
const fetchYoutubeSearch = async ({query}) => {
console.log(query)
try {
const {data} = await youtube.search.list({
key: process.env.YOUTUBE_API_TOKEN,
part: ['snippet'],
q: query,
channelId: 'UCBh8XcZST_JTHt-IZDxT_pQ',
order: 'date',
type: 'video',
maxResults: 50
})
console.log('YOUTUBE SEARCH', data)
return data.items
} catch(err) {
console.log(chalk.red(err))
}
}
/******************** LIST TO PORT *********************/
const port = process.env.PORT || 3001;
app.listen(port, () => console.log(`Listing on port ${port}`));
Is the issue that your browser is blocking CORS? That happens to me with Heroku stuff sometimes. There are browser extensions to block/unblock CORS depending on the browser you're using
Stick only with app.use(cors()); that alone should work fine. Instead double check your Config Vars (env vars) on heroku and/or netlify wherever you set such variables. Sometimes that CORS error can be misleading being actually a connection error more about your environment variables.

Socket IO CORS issue when using a domain for server

I am having errors from socket io when I call my server from a domain instead of server IP. This domain works for fetching data by Axios but fails and shows the error below for socket:
Access to XMLHttpRequest at
'https://api.alo.social/socket.io/EIO=3&transport=polling&t=NA9Kd2D' from
origin 'https://apps.alo.social' has been blocked by CORS policy: The value of the 'Access-
Control-Allow-Credentials' header in the response is '' which must be 'true' when the
request's credentials mode is 'include'. The credentials mode of requests initiated by the
XMLHttpRequest is controlled by the withCredentials attribute.
GET https://api.alo.social/socket.io/?EIO=3&transport=polling&t=NA9Kd2D net::ERR_FAILED
My server code looks like this:
const express = require('express')
const app = express()
const bodyParser = require('body-parser')
const cors = require('cors')
const server = require('https').createServer(app)
const getRoute = require('./routes/getRoutes')
const postRoute = require('./routes/postRoutes')
const socketIO = require('socket.io')
const io = socketIO(server)
require('dotenv').config()
let port = process.env.PORT
let whitelist = [
'https://apps.alo.social',
'https://api.alo.social'
]
let corsOptions = {
origin(origin, callback) {
if (whitelist.indexOf(origin) !== -1 || !origin) {
console.log('Okay', origin)
callback(null, true)
} else {
console.log('Not okay', origin)
callback(new Error('Not allowed by CORS'))
}
}
}
app.options('*', cors())
app.use(cors(corsOptions));
app.use(express.static('public'))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
io.on('connection', function (socket) {
app.use(function (req, res, next) {
res.io = io
next()
})
socket.on('pushMessage', function (data) {
// console.log('pushMessage socket', data)
io.emit('pushMessage', data)
})
})
app.get('/', (req, res) => {
res.send(`Server is up into port ${port}`)
})
app.use('/api/get', getRoute)
app.use('/api/post', postRoute)
app.listen(port, () => console.log(`Server is up into port ${port}`))
module.exports = { app: app, server: server }
The client end where I am defining the socket is like:
import socketIOClient from 'socket.io-client'
let serverSocket = 'https://api.alo.social/'
let socket = socketIOClient(serverSocket)
and calling the socket like:
socket.emit('pushMessage', messageData)
socket.on('pushMessage', messageData => { console.log(messageData) })
Can anyone help with this?
It solved after modifying io configuration in server.js as:
const io = module.exports.io = require('socket.io').listen(server, {
handlePreflightRequest: (req, res) => {
const headers = {
"Access-Control-Allow-Headers": "Content-Type, Authorization",
"Access-Control-Allow-Origin": '*',
};
res.writeHead(200, headers);
res.end();
}
})
server.listen(port)

CORS origin undefined with simple nodejs server

I have a very simple nodejs server, but using the 'cors' package seems to not recognize the origin of the request for some reason.
Here is my nodejs server config:
const cors = require('cors');
const express = require('express');
const CORS_WHITELIST = [ 'http://localhost:5000' ];
const corsOptions = {
origin: (origin, callback) => {
console.log(origin) // ----> this is always 'undefined'
if (CORS_WHITELIST.indexOf(origin) !== -1){
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
};
const configureServer = app => {
app.use(cors(corsOptions));
};
module.exports = configureServer;
Here is my server starter file:
const express = require('express');
const SERVER_CONFIGS = require('./constants/server');
const configureServer = require('./server');
const configureRoutes = require('./routes');
const app = express();
configureServer(app);
configureRoutes(app);
app.listen(SERVER_CONFIGS.PORT, error => {
if (error) throw error;
console.log('Server running on port: ' + SERVER_CONFIGS.PORT);
});
I am running this server on localhost, but the origin in the cors callback is always 'undefined'. For example when I open http://localhost:5000 on the browser or do a curl call.
How can I use cors so that it doesn't block the request on localhost?
I read this issue and req.headers.origin is undefined question and also CORS and Origin header!
source:
The origin may be hidden if the user comes from an ssl encrypted website.
Also: Some browser extensions remove origin and referer from the http-request headers, and therefore the origin property will be empty.
There is a solution to solve this by adding a middleware:
app.use(function (req, res, next) {
req.headers.origin = req.headers.origin || req.headers.host;
next();
});
I hope these helps. The issue is in awaiting more info state!

HTTPS issue node server MERN stack

I have been frantically trying for hours to get my email working.
This is the website:https://www.shafirpl.com/contact
I have a react app hosted on the same server ( a digital ocean droplet) as node.js app. The domain name(shafirpl.com) has SSL certificate from cloudflare. The node.js app is running on port 4000 while the react app on port 80. So what is happening now is that the react production build is running on port 80 of that IP address/server, and I have an axios post request when the user clicks the send button. When it was on my local machine it worked as the axios request was using "http://localhost:4000/email". But when I deployed on the server and changed the URL to "http://myServerIpAddress:4000/email" I get the error that says I have to send the request via https. I am not sure how to generate an SSL certificate so that my front end react app can commit the axios request and don't have the issue. I have tried to follow certbot tutorial but it seems like certbot requires a specific domain name. SO what I did is that I created key-cert pairs for my domain name (shafirpl.com) using this tutorial (https://dev.to/omergulen/step-by-step-node-express-ssl-certificate-run-https-server-from-scratch-in-5-steps-5b87) and am using in my server.js file (the node.js app brain) like this:
const express = require("express");
// const connectDB = require("./config/db");
const path = require("path");
const https = require("https");
const fs = require("fs");
// routes variables
const emailRoute = require("./routes/email");
const resumeRoute = require("./routes/resume");
// const authRoute = require("./routes/api/auth");
const app = express();
var cors = require("cors");
// var corsOptions = {
// origin: "*",
// optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
// };
app.use(cors());
app.options("*", cors());
// Connect Database
// connectDB();
// Middleware initialization
/*
* Usually we used to install body parser and do
* app.use(bodyparser.json()). But now bodyparser comes
* packaged with express. So we just have to do express.json()
* to use bodyparser
*/
app.use(express.json({ extended: false }));
// use this when on my pc
// app.use(function (req, res, next) {
// res.header("Access-Control-Allow-Origin", "http://localhost:3000"); // 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();
// });
// use this on produnction
// app.use(function (req, res, next) {
// res.header("Access-Control-Allow-Origin", "*"); // 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();
// });
// app.get("/", (req,res) => {res.send('API Running')});
// Define Routes
app.get("/", (req, res) => {
res.send("Server Running");
});
app.use("/email", emailRoute);
app.use("/resume", resumeRoute);
// app.use("/api/auth", authRoute);
// app.use("/api/profile", profileRoute);
// app.use("/api/posts", postsRoute);
// // serve static assets in production
// if (process.env.NODE_ENV === "production") {
// // set static folder
// app.use(express.static("client/build"));
// app.get("*", (req, res) => {
// res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
// });
// }
/*
* This means when the app will be deployed to heroku, it will
* look for a port specified by heroku. But since right now
* locally we don't have that, we will be running the app on
* port 5000
*/
// const PORT = process.env.PORT || 4000;
// app.listen(PORT, () => {
// console.log(`Server started on port ${PORT}`);
// });
app.listen(4000);
// comment out this line when testing on localhost
const httpsServer = https.createServer(
{
key: fs.readFileSync("/etc/letsencrypt/live/shafirpl.com/privkey.pem"),
cert: fs.readFileSync("/etc/letsencrypt/live/shafirpl.com/fullchain.pem"),
},
app
);
httpsServer.listen(443, () => {
console.log("HTTPS Server running on port 443");
});
And in my axios.post I am using like this
const url = "https://shafirpl.com:443/email";
const sendMessage = async () => {
const config = {
headers: {
'Content-Type': 'application/json',
}
}
const body = JSON.stringify({ name, email, company, message });
try {
const res = await axios.post(url, body, config);
console.log(res);
clearForm();
showSuccessMessage();
} catch (error) {
console.log(error);
showFailureMessage();
}
}
const showFailureMessage = () => {
setFailureAlert(true);
setTimeout(() => {
setFailureAlert(false)
}, 3000);
}
But right now I am again getting this error:
Access to XMLHttpRequest at 'https://shafirpl.com/email' from origin 'https://www.shafirpl.com' 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 actually don't know how to solve this as I am pretty new to the total MERN stack build. Can anyone help me with this? I just wanna send email using the axios
I had the same issue - what I did, I removed explicit ports from both server and client. Then I noticed that I was hitting http://mydomain.... please try accessing it from https://mydomain... that did the trick for me :) Hope it helps!
I think i fixed the issue. Instead of running 2 different application, I decided to serve my react build via my node.js app. The way I did was this:
const express = require("express");
// const connectDB = require("./config/db");
const path = require("path");
// routes variables
const emailRoute = require("./routes/email");
const resumeRoute = require("./routes/resume");
const app = express();
app.use(express.json({ extended: false }));
app.use("/api/email", emailRoute);
app.use("/api/resume", resumeRoute);
app.use(express.static("client/build"));
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
});
app.listen(80);
Then on my axios request I just did that:
const url = "/api/email"; const sendMessage = async () => {
const config = {
headers: {
'Content-Type': 'application/json',
}
}
const body = JSON.stringify({ name, email, company, message });
try {
const res = await axios.post(url, body, config);
console.log(res);
clearForm();
showSuccessMessage();
} catch (error) {
console.log(error);
showFailureMessage();
}
}
Right now everything is working fine.
For the resume thing which sends a file download, instead of using /api/resume I had to do
something like this
<Nav.Link eventKey="6" activeClassName="active-nav" href="https://shafirpl.com/api/resume" target="_blank" rel="noopener noreferrer">Resume</Nav.Link>
And right now the resume download is also working fine
Thanks for all the help

Resources