express + cors() is not working properly - javascript

I'm building a React application and I'm trying to make a call to https://itunes.apple.com/search?term=jack+johnson
I have a helper called requestHelper.js which looks like :
import 'whatwg-fetch';
function parseJSON(response) {
return response.json();
}
function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
}
const error = new Error(response.statusText);
error.response = response;
throw error;
}
export default function request(url, options) {
return fetch(url, options)
.then(checkStatus)
.then(parseJSON);
}
So I get:
XMLHttpRequest cannot load
https://itunes.apple.com/search?term=jack%20johnson. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:3000' is therefore not allowed
access.
My express server looks like this:
const ip = require('ip');
const cors = require('cors');
const path = require('path');
const express = require('express');
const port = process.env.PORT || 3000;
const resolve = require('path').resolve;
const app = express();
app.use(cors());
app.use('/', express.static(resolve(process.cwd(), 'dist')));
app.get('*', function(req, res) {
res.sendFile(path.resolve(resolve(process.cwd(), 'dist'), 'index.html'))
});
// Start app
app.listen(port, (err) => {
if (err) {
console.error(err.message);
return false;
}
const divider = '\n-----------------------------------';
console.log('Server started ✓');
console.log(`Access URLs:${divider}\n
Localhost: http://localhost:${port}
LAN: http://${ip.address()}:${port}
${divider}
`);
});
I have tried using mode: 'no-cors' but is not actually what I need since the response is empty.
Am I doing something wrong with this configuration?

The same origin policy kicks in when code hosted on A makes a request to B.
In this case A is your Express app and B is iTunes.
CORS is used to allow B to grant permission to the code on A to read the response.
You are setting up CORS on A. This does nothing useful since your site cannot grant your client side code permission to read data from a different site.
You need to set it up on B. Since you (presumably) do not work for Apple, you can't do this. Only Apple can grant your client side code permission to read data from its servers.
Read the data with server side code instead.

Related

Why am I getting a No 'Access-Control-Allow-Origin' error?

Chrome authorization Headers
I believe I have cors configured correctly in my backend. The API is hosted on Heroku if that helps. Here is the server.js file:
const express = require("express");
const helmet = require("helmet");
const cors = require("cors");
const bodyParser = require("body-parser");
const productsRouter = require("./products/products-router");
const ordersRouter = require("./orders/orders-router");
const emailsRouter = require("./emails/emails-router");
const corsOptions = {
origin: "*",
credentials: true,
optionSuccessStatus: 200,
};
const server = express();
server.use(express.json());
server.use(helmet());
server.use(cors(corsOptions));
server.use("/api/products", productsRouter);
server.use("/api/orders", ordersRouter);
server.use("/api/emails", emailsRouter);
server.use((err, req, res, next) => {
res.status(err.status || 500).json({
message: err.message,
});I
});
module.exports = server;
I get a cors error when my front-end tries to make HTTP requests to the backend. It reads as follows:
Access to XMLHttpRequest at
'https://nanasoapsbackend.herokuapp.com/api/products/categories' from
origin 'http://localhost:3000' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
Yes, my back and and front end are on different domains. This just happened all of a sudden, it was working fine with no cors errors for the past few months, and suddenly it stopped working. Any help would be greatly appreciated.
The problem is: You can't have origin * (allow everything) with allow credentials. See: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSNotSupportingCredentials
You have to set up a white list (list of domains that are allowed)
const whitelist = ['http://www.example.com', 'http://www.otherexample.com']
const corsOptions = {
origin: function (origin, callback) {
if (whitelist.includes(origin)) {
callback(null, true)
} else {
callback(new Error('Not allowed'))
}
}
}
Then on your client side (frontend) make sure you use withCredientals flag on your http agent (axios, superagent, fetc, etc)

socket.io, vTiger, and csrf-magic.js. CORS issues

I'm attempting to create and add a socket.io module to my vTiger 7.0 so that I can update fields in real-time to multiple users.
We are have issues with users changing fields that should be locked while our quality control is attempting to check the record. This is causes things to get approved that should not. Node.js with vTiger will be awesome add-on.
The only problem is that vTiger uses csrf-magic.js to create a token that need to be included in the header to allow CORS
I have the middleware setup in my node project to allow my vtiger to make a request
vTiger is on vtiger.example.com
The node server is on node.example.com:3010
//server code node.example.com:3010
const fs = require("fs");
const config = require("./core/config").config();
var options = {
key: fs.readFileSync(config.key),
cert: fs.readFileSync(config.cert),
ca: fs.readFileSync(config.ca),
requestCert: true,
rejectUnauthorized: false,
};
const app = require("express")();
const server = require("https").Server(options, app);
const io = require("socket.io")(server);
// Need to send io to socket module
module.exports = io;
app.use(function (req, res, next) {
var allowedOrigins = [
"https://node.example.com",
"https://vtiger.example.com"
];
var origin = req.headers.origin;
if (allowedOrigins.indexOf(origin) > -1) {
res.setHeader("Access-Control-Allow-Origin", origin);
}
res.header("Access-Control-Allow-Methods", "GET, OPTIONS");
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
res.header("Access-Control-Allow-Credentials", true);
return next();
});
io.sockets.on("connection", require("./sockets/socket.js"));
const qc = require('./models/qc_model');
app.get('/', (req,res) => {
res.json({message: 'No Access'});
})
qc.pullLeadInfo(13622196, 10730, (data) => {
console.log(data.lead.lsloa_ver_by);
});
//Start the server
server.listen(config.port, () => {
console.log("server listening on port: " + config.port);
});
\\client side vtiger.example.com
var socket = io.connect('https://node.example.com:3010');
I get this error
Access to XMLHttpRequest at 'https://node.example.com:3010/socket.io/?EIO=4&transport=polling&t=NmEEc_r' from origin 'https://vtiger.example.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.
csrf-magic.js:41 GET https://node.example.com:3010/socket.io/?EIO=4&transport=polling&t=NmEEc_r net::ERR_FAILED
I cannot find any good documentation dealing with this issue. Any help would be great!
Found the information here
https://github.com/socketio/socket.io/issues/3929
// Server
io.engine.on("headers", (headers) => {
headers["Access-Control-Allow-Private-Network"] = true;
});
// Client
const socket = io({
extraHeaders: {
"Access-Control-Request-Private-Network": true
}
});

react client wont follow redirect CORS localhost

front end: localhost:3000 (react app) App.js (client) on load call api '/'
function fetch_redirect() {
fetch("http://localhost:8082")
}
function App() {
return <div className="App">{fetch_redirect()}</div>;
}
export default App;
backend: localhost:8082 (nodejs express app) send redirect to /test endpoint on client
const express = require('express')
const app = express()
const cors = require('cors');
const whitelist = ['http://localhost:3000']
const corsOptions = {
origin: function (origin, callback) {
console.log(origin, whitelist.indexOf(origin) !== -1);
if (whitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
}
app.use(cors(corsOptions))
app.get('/', function (req, res) {
res.redirect('http://localhost:3000/test')
})
app.listen(8082)
The main issue is on the client (react) once the API call is made, the redirect is blocked... how can I make the client react app follow this redirect to 'http://localhost:3000/test' I already have cors on my backend with an enabled whitelist of localhost:3000 to allow me to make the API call not blocked... however the redirect is now blocked from the fetch frontend!
error details:
Access to fetch at 'http://localhost:3000/test' (redirected from 'http://localhost:8082/') 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.
In order to facilitate the development and have a development environment closer to what the production environment should be (no need to decrease the security level with Access-Control-Allow-Origin), you should have a unique entry point to your frontend and backend, with a unique origin (http://localhost:3000 in your case).
To achieve that, remove all of the Access-Control-* headers, create a src/setupProxy.js file and fill it like this:
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = app => {
app.use(
"/api",
createProxyMiddleware ({
target: "http://localhost:8082",
changeOrigin: true
})
);
};
Then install the needed express middleware as a development dependency with npm install --save-dev http-proxy-middleware
Note: this feature is available with react-scripts#2.0.0 and higher.
Finally, replace all of the fetch from the frontend with relative URLs starting with /api
fetch("http://localhost:8082") becomes fetch('/api'))
Something like fetch('http://localhost:8082/some-endpoint') would become fetch('/api/some-endpoint')
For more information about this, please refer to the docs.
Try to refactor your code like this:
const express = require('express')
const app = express()
const cors = require('cors');
const whitelist = ['http://localhost:3000', 'http://localhost:8082'];
const corsOptionsDelegate = (req, callback) => {
var corsOptions;
if (whitelist.indexOf(req.header('Origin')) !== -1) {
corsOptions = { origin: true };
} else {
corsOptions = { origin: false };
}
callback(null, corsOptions);
};
const corsWithOptions = cors(corsOptionsDelegate);
app.route('/')
.options(corsWithOptions, (req, res) => { res.sendStatus(200); })
.get(cors(), (req, res)=>{
res.redirect('http://localhost:3000/test')
});
app.listen(8082)
Try using next function inside app.get:
app.get('/', function (req, res, next) {
res.redirect('http://localhost:3000/test');
next();
})
Also for your ReactJS application (I'm not sure about your implementation, CSR or SSR) you should add two things:
Webpack: proxy for passing CORS
CSR:
devServer: {
...
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
"Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
}
}
SSR: add the above header to webpackDevMiddleware options.
Route:
CSR: for running the project in the dev side use:
devServer: {
historyApiFallback:{
index:'build/index.html'
},
},
for the prod side use spa flag for running, for example for serve use serve -s ./dist or for pm2 run pm2 start ./dist --spa.
SSR: no need to add extra configuration.
try something like this instead:
whitelistedOrigins = ['http://localhost:3000'];
app.use(cors({
origin: whitelistedOrigins,
methods: ['GET', 'PATCH', 'PUT', 'POST', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
}));
you can modify the methods/allowedHeaders keys as you want...
Edit: your backend is :3000, isn't it the problem? the client can't be at 3000 at the same time.

cors package send status instead of error

I am trying to use cors package in node and express environment to verify if requesting domain can access my resources. That part was not a problem following official documentation. The problem however is with response. If domain is not allowed to access - cors is sending error with stack trace (paths to files). Can this be avoided and just respond with status 401, something like res.status(401).end() ? I tried that but it gives me error because headers were already sent.
const cors = require("cors");
const corsOptions = async (req, callback) => {
let domain = getDomain(req);
if (domain === false) {
return callback(new Error("Not allowed by CORS"));
}
const isWhitelisted = await client.get(domain).catch(err => { console.log(err); });
if (isWhitelisted !== undefined) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"));
}
};
app.use(cors(corsOptions));
So i was hoping to replace callback(new Error("Not allowed by CORS")); this part with just sending status 401 and ending stream so no errors with stack are printed in the client.
It can't be done, according to the source code what you are sending in callback as err is passed in the next function like
if(err) next(err);
But there is one easy workaround to what you want.
The cors() is returning a middleware(a function with three params req, res and next). So you can wrap the cors in your middleware and return whatever you want. If all the things are perfect you can use the cors.
const app = require('express')();
const cors = require("cors");
const corsMid = (req, res, next)=>{
const trueVal = true;
if(trueVal){
res.status(400).send("Error")
}else{
return cors()(req,res,next);
}
}
app.use(corsMid);
app.get('/', (req, res)=>{
res.json('Hello World');
})
app.listen(8080, ()=>{
console.log('started')
})
I found a workaround is you can construct the Error on a variable first, then remove the stack property from the error variable:
let err = new Error('Not allowed by CORS')
err.stack = ""
callback(err)
Hope this help!

HTTPS Redirect in next js

I'm trying to do a http to https redirect using next,
so if the user enters the site at http://www.example.com redirect to https://www.example.com
I'm using GraphQL Yoga on the server side, so not sure how I could accomplish this in my index file on the server side.
I've tried using the meta tag and changing the protocol in the window object but no luck with doing so in server-side rendering.
Is there any way I can accomplish this redirect on the client side using next js or on the server side?
const cookieParser = require('cookie-parser')
const jwt = require('jsonwebtoken')
require('dotenv').config({path: '.env'})
const createServer = require('./createServer')
const db = require('./db')
const sslRedirect = require('heroku-ssl-redirect');
const server = createServer()
//Express middleware to handle cookies
server.express.use(cookieParser())
//decode JWT
server.express.use((req, res, next) => {
const { token } = req.cookies;
if (token) {
const { userId } = jwt.verify(token, process.env.APP_SECRET);
req.userId = userId;
}
next()
})
//Populates user on request
server.express.use(async (req, res, next) => {
if(!req.userId) return next()
const user = await db.query.user({
where: {id: req.userId}
}, `{id, permissions, email, name}`)
req.user = user
next()
})
//start
server.start({
cors: {
credentials: true,
origin: process.env.FRONTEND_URL
},
}, starting => {
console.log(`server is running on port ${starting.port}`)
})
What I have done in the past is started both a HTTP and a HTTPS server with express.
The HTTPS is the server with all the routes\API's configured.
The HTTP server simply to redirects all GET requests to HTTPS. See the following code which could be used setup the HTTP server to do the redirect.
let httpRedirectServer = express();
// set up a route to redirect http to https
httpRedirectServer.get('*', (request, response) => {
response.redirect('https://' + request.headers.host + request.url);
});
httpRedirectServer.listen(80);
httpRedirectServer.on('listening', () => {
console.log("Listening to redirect http to https");
});
Alternatively on the client side a quick fix is to redirect in javascript by running something like.
// Check the URL starts with 'http://xxxxx' protocol, if it does then redirect to 'https://xxxxx' url of same resource
var httpTokens = /^http:\/\/(.*)$/.exec(window.location.href);
if(httpTokens) {
window.location.replace('https://' + httpTokens[1]);
}

Categories

Resources