Why can't I connect to localhost server from localhost client (cors error)? - express

I have a local (Angular) client running on port 4200 (http://localhost:4200) and a local (express) server on port 5000 (http://localhost:5000). Whenever I try to connect to my server, I get this message.
Access to XMLHttpRequest at 'http://localhost:5000/socket.io/?EIO=4&transport=polling&t=NU7H' from origin
'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Here is the code to start my local server
#injectable()
export default class App {
app: express.Application;
constructor() {
this.app = express();
this.config();
this.bindRoutes();
}
// Middlewares config
private config(): void {
this.app.use(cors());
this.app.use(express.json());
this.app.use(express.urlencoded({ extended: true }));
}
bindRoutes(): void {
this.app.use('/', router);
}
}
Here is the code where I set up my socket
private _ioServer: SocketIO.Server;
initSocket(server: http.Server) {
this._ioServer = new SocketIO.Server(server);
this.connectChat(); // Chat namespace
this.connectStream(); // Game board streaming namespace
}
I tried with Postman, everything is working.
Thank you!

Any malicious site can take advantage of your cookies stored in the system called Cross-site request forgery
Any browser tries to prevent you from these attacks so they disable CORS.
Shorthand Fix [Not recommended] : There are many plugins out there you can use for your local testing that disables these checks on browser.
Proper Fix: Use an Express middleware to apply Access-Control-Allow-Origin: * in your header when response is returned back from the server.
Gist is that when browser sends the request to your server it will append Origin: http://localhost:3000 to the headers. Reacting to this request from browser, server should return a Access-Control-Allow-Origin header to specify which origins can access the server's resources.
You can be strict here to return Access-Control-Allow-Origin: http://localhost:4200 or open your gates by sending Access-Control-Allow-Origin: *.
Here is the quick code to have an express middleware:
const express = require('express');
const request = require('request');
const app = express();
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
next();
});
app.get('/jokes/random', (req, res) => {
request(
{ url: 'https://joke-api-strict-cors.appspot.com/jokes/random' },
(error, response, body) => {
if (error || response.statusCode !== 200) {
return res.status(500).json({ type: 'error', message: err.message });
}
res.json(JSON.parse(body));
}
)
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`listening on ${PORT}`));
Source: https://medium.com/#dtkatz/3-ways-to-fix-the-cors-error-and-how-access-control-allow-origin-works-d97d55946d9
P.S, this is a very good read for your understanding of CORS.

In the index.js file of your middleware add:
app.use(cors())

Related

How to proxy data without transfer-encoding: chunked?

I have an express.js-based proxy that I use to proxy various separate apps deployed via Serverless and AWS Lambda into the same domain:
const serverless = require('serverless-http');
const httpProxy = require('http-proxy');
const express = require('express');
const app = express();
const proxy = httpProxy.createProxyServer();
proxy.on('proxyReq', (proxyReq) => {
proxyReq.setHeader('X-Projects-Router-Proxy-Out', true);
});
app.get(['/:project', '/:project/*'], (req, res) => {
const { project } = req.params;
const rest = req.params[0];
const url = `https://${project}.${process.env.DEPLOY_STAGE}.example.com/${project}`;
req.url = rest ? `/${rest}` : '';
proxy.web(req, res, {
target: url,
xfwd: false,
toProxy: true,
changeOrigin: true,
secure: true,
});
});
module.exports.handler = serverless(app);
This works very well for most things, but I'm now having trouble. One of the projects I'm proxying is a next.js app, and the _next directory's assets all return a 502 Bad Gateway error because chunked encoding is not supported by AWS API Gateway (which I'm using to handle all the requests). According to some google results, I ought to just be able to removed the transfer-encoding: chunked header.
Which would be fine, except there is no transfer-encoding header being sent on these assets, so removing it does nothing to fix the problem. I think there is also no content-length header being sent, so it assumes chunked by default. How do I add a content-length header? Is there another way of getting around this problem?

Cookies included in request header, but express returns empty cookies (using Amazon Load Balancer)

I'm doing fetch from my frontend to my express backend. But express logs req.cookies as '' (empty). I'm using cookieParser.
Why is express not finding the cookie, even though the browser shows the cookies being sent?
Note: I'm using cookies forwarded by my load balancer, which does the authentication and sends the session over.
Frontend
fetch(`${MY_URL}/logout`, {
credentials: 'include',
})
NodeJS
const cookieParser = require("cookie-parser");
app.use(cookieParser());
app.get("/logout", (req, res, next) => {
console.log(req.headers) // see below
console.log(JSON.parse(JSON.stringify(req.cookies))); // logs {}
console.log(JSON.parse(JSON.stringify(req.signedCookies))); // logs {}
// do stuff with cookie
});
Headers
{
...
cookie: ''
}
Cookie in Headers is an empty string
Network tab:
Got this working. Eventually the solution was that the Load balancer automatically forwards these headers to the backend silently. For my /logout api, instead of trying to grab the cookies from the headers, I set them regardless. Something like this:
app.get('/logout', (req, res) => {
res.cookie("AWSELBSessionCookie", "", {
maxAge: -1,
expires: Date.now(),
path: '/'
}
res.setHeader("Cache-Control", "must-revalidate, no-store, max-age=0");
res.setHeader("Pragma", "no-cache");
res.setHeader("Expires", -1);
res.redirect("https://my-login-page.com");
})

Posting to backend but request always empty (vue to node)

I have a very simple user backend up and running (node, express, mongoose, mongo, etc) and with postman can verify when I add a user it works, when I request a login it works and get a token, and if I put in the wrong details it rejects it,
Now I used this git hub repo https://github.com/christiannwamba/vue-auth-vuex to spin up a simple frontend for this. Which I thought was all working fine as it appeared to be logging in until I found it was accepting whatever details I put in for the email and password as correct!
The backend server kept responding ok when I hit it with the vue app, but on closer inspection when I console logged what it was getting, which was null and returning user not found. So again I don't think there is anything wrong here.
Something I have noticed in chrome dev tools network, it is sending two versions of authenticate, first is empty and then the next one has responses.
I'm at a bit of a loss why it's sending empty requests first time and why it allows the login when it's getting a bad return.
Server.js file:
const express = require('express');
const logger = require('morgan');
const movies = require('./routes/movies') ;
const users = require('./routes/users');
const bodyParser = require('body-parser');
const mongoose = require('./config/database'); //database configuration
var jwt = require('jsonwebtoken');
var cors = require('cors')
const app = express();
// Add cors
app.use(cors());
app.options('*', cors()); // enable pre-flight
app.set('secretKey', 'nodeRestApi'); // jwt secret token
// connection to mongodb
mongoose.connection.on('error', console.error.bind(console, 'MongoDB connection error:'));
app.use(logger('dev'));
app.use(bodyParser.urlencoded({extended: false}));
app.get('/', function(req, res){
res.json({"api" : "User API"});
});
// public route
app.use('/users', users);
// private route
app.use('/movies', validateUser, movies);
app.get('/favicon.ico', function(req, res) {
res.sendStatus(204);
});
function validateUser(req, res, next) {
jwt.verify(req.headers['x-access-token'], req.app.get('secretKey'), function(err, decoded) {
if (err) {
res.json({status:"error", message: err.message, data:null});
}else{
// add user id to request
req.body.userId = decoded.id;
next();
}
});
}
// express doesn't consider not found 404 as an error so we need to handle 404 it explicitly
// handle 404 error
app.use(function(req, res, next) {
let err = new Error('Not Found');
err.status = 404;
next(err);
});
// handle errors
app.use(function(err, req, res, next) {
console.log(err);
if(err.status === 404)
res.status(404).json({message: "Not found"});
else
res.status(500).json({message: "Something looks wrong :( !!!"});
});
app.listen(3000, function(){
console.log('Node server listening on port 3000');
});
Update:
I have added in under my CORS bit in server.js:
app.options('/users/authenticate', function(req, res){
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'POST');
res.end();
});
In network I now only get the one request. Under form data it appears to be there but it's saying in the response that data is null, and even more odd the vuejs is still logging in and allowing access to the restricted pages.
Temporarily comment out the line where you set the headers to application/x-www-form-urlencoded. Then add app.use(bodyParser.json()) to your server.js and see if it works. What's happening is your request object is malformed, which is why the server cannot parse the request correctly.
Looks like CORS issue. If you run UI using a different server and your backend is running by itself, then your browser will send pre-flight request first which is an options request. That is the reason you see 2 authenticate requests in the developer tools. You can read more about this here
Why is an OPTIONS request sent and can I disable it?

Express middleware not called when accessing socketio endpoint

I have an application where I want to avoid robots to try to use my socket.io endpoint.
My socket.io sits on top of express:
const app = require('express')();
app.use(blockRobots);
const io = require('socket.io')(app{path: '/socket'});
If I access this server to any path except /socket, the middleware is executed.
However, doing a (GET) request to /socket does not trigger the middleware.
Any ideas?
Without delving into the code, I assume that socket.io attaches a listener to the HTTP server that gets triggered before Express gets to handle the request at all.
You can use the allowRequest option for socket.io to reject unwanted requests:
const io = require('socket.io')(app, {
path: '/socket',
allowRequest: (req, callback) => {
if (CHECK_FOR_ROBOT) {
return callback(null, false);
} else {
return callback(null, true);
}
}
});

CORS --- Response to preflight request doesn't pass access control check

I'm having difficulty with getting CORS to work. I'm trying to fetch data from a Heroku app I set up. I'm fetching from Redux, and using ExpressJS for the backend. I've been looking through the CORS docs but still can't figure it out. enter link description here
This is how it looks in Express
var express = require('express');
var router = express.Router();
var cors = require('cors')
router.options('/', cors())
router.use((req, res, next) => {
res.append('Access-Control-Allow-Origin', ['*']);
res.append('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.append('Access-Control-Allow-Headers', 'Content-Type');
next();
});
router.get('/', cors(), function(req, res, next) {
res.json({...})
}
I have the proxy set up in my app to http://localhost:3001/, but my app is running on 3000. I'm including this in case it could be the issue, but I don't know that it is.
My Redux file is set up like this
return dispatch => {
const url = "https://app-name.herokuapp.com/users";
return fetch(url, {
method: 'GET',
mode: 'cors',
headers: { 'Content-Type': 'text' }
})
The full error is: "Failed to load https://app-name.herokuapp.com/users: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access."
var express = require('express');
var app = express();
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET,POST");
// res.header("Access-Control-Allow-Headers", "Content-Type");
next();
});