Just can't resolve CORS issue - node.js

I just can't make calls to my fully functional API because I keep getting this error -
angular.js:9827 **OPTIONS http://xyz.mybluemix.net/add_user** (anonymous function)
# angular.js:9827sendReq # angular.js:9628serverRequest
# angular.js:9344processQueue # angular.js:13189(anonymous function)
# angular.js:13205Scope.$eval # angular.js:14401Scope.$digest
# angular.js:14217Scope.$apply # angular.js:14506(anonymous function)
# angular.js:16232completeOutstandingRequest
# angular.js:4905(anonymous function)
# angular.js:5285
welcome.html:1 **XMLHttpRequest cannot load http://xyz.mybluemix.net/add_user.
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:9000' is therefore not allowed access.
The response had HTTP status code 502.**
Here is the code I am running:
//Client side code (Angular JS)
dataFactory.makeUser(user)
.then(function (response) {
console.log('User created');
window.location = "index.html";
}, function(error) {
console.log('User failed to create: ' + error.message);
});
app.factory('dataFactory', ['$http', function($http) {
var base = 'xyz';
var dataFactory = {};
//works fine
dataFactory.getAllUsers = function () {
return $http.get(base+"get_all_users");
};
//works fine
dataFactory.getUserById = function (id) {
return $http.post(base+"get_user_byId", id);
};
//the request with the problem
dataFactory.makeUser = function (user) {
return $http.post(base+"add_user", user);
};
return dataFactory;
}]);
//Server-side code
var express = require('express');
var bodyParser = require('body-parser');
var mysql = require('mysql');
var app = express();
var cors = require('cors');
app.use(cors());
app.post('/add_user', function(req, res) {
var id = req.body.id;
var name = req.body.name;
var gender = req.body.gender;
var email = req.body.email;
var age_min = req.body.age_min;
var age_max = req.body.age_max;
var hometown = req.body.hometown;
var picture = req.body.picture;
var user = {
id: id,
name: name,
gender: gender,
email: email,
age_min: age_min,
age_max: age_max,
hometown: hometown,
picture: picture
};
connection.query('INSERT INTO users SET ?', user, function(err, rows) {
if (err) {
res.json(err);
return;
}
res.json(rows);
});
});

It appears you're trying to do a cor from your web domain to localhost and you haven't explicitly told the server, localhost, that this type of request is ok. The server refuses to shake hands and comes back (preflight) saying that business is a no-go.
I am not too familiar with npm-cors, and according to the documentation your implementation looks correct.
express comes with the ability to control cor, which is what you're trying to do:
app = express();
app.use(function(req, res, next) {
//
//allow cor on origin (use req.headers.origin to allow everything,
//very discouraged since you're api only communicates with your site)
//
res.header('Access-Control-Allow-Origin', 'http://xyz.mybluemix.net');
//if you are passing creds, which you're not
//res.header('Access-Control-Allow-Credentials', true);
//methods allowed
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
//headers allowed
res.header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept');
//continue on with the request
next();
});
I forgot to mention to make sure your base http in has the port attached to it:
$http.post('http://xyz.mybluemix.net:9000/post', data);

Related

Internal Server Error 500 from API when deploying front-end React.js backend Express app

For my senior capstone, my group and I have developed a web-based application to simulate Bitcoin - using react.js for the front-end and node.js/express for the back-end. Up until recently, we've had all of simulation-creating-code (javascript files) inside the src directory, meaning it was being built client-side. Due to high waiting times to create a simulation from all the hashing necessary in transactions, we decided that our simulation-creating-code would be better suited for the back-end rather than the front end. Taking the load off the client and putting it on the server drastically improved the speed of creating a simulation, so 'Great success!'.
When we made this change, we ended up having some issues with require and import statements. Reactjs only supports import statements and Express uses require statements. We had to use some js functions that we developed in our API's so we imported them with require statements, and we thought we thought it was resolved because on our development environment, everything runs as smooth as butter, but once it's deployed, our login page is unable to make an API call. The error is: Failed to load resource: the server responded with a status of 500 (Internal Server Error).
It's interesting because this route in the API worked prior to making this big switch from require to import, and those changes were in other files/routes. The login API remains completely unchanged.
Either way, I'll drop some code in case it's helpful in troubleshooting.
server.js
const express = require("express");
const app = express();
const router = express.Router();
const path = require("path");
var cors = require("cors");
require("dotenv").config();
app.use(express.json({ limit: "50mb" }));
app.use(express.urlencoded({ limit: "50mb" }));
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
// List of routes
router.use("/api/users", require("./api/users"));
router.use("/api/data", require("./api/data"));
router.use("/api/share", require("./api/share"));
router.use("/api/addresses", require("./api/addresses"));
const root = path.join(__dirname, "client/build");
app.use(express.static(root));
app.use(router);
app.use(cors({ origin: true, credentials: true }));
app.listen(
process.env.PORT,
() => `Server running on port ${process.env.PORT}`
);
api/users.js login route
const express = require("express");
const app = express();
const db = require("../dbConn");
const bcrypt = require("bcrypt-nodejs");
const cors = require("cors");
const router = express.Router();
const jwt = require("jwt-simple");
const config = require("../configuration/config.json");
// to parse JSON
app.use(express.json());
router.post("/login", (req, res) => {
//check if email and password are sent
if (!req.body.email || !req.body.password) {
return res.status(401).json({ error: "Missing username and/or password" });
}
// go into mysql and get info
let qry = `select * from user where email = "${req.body.email}"`;
db.query(qry, (err, rows) => {
if (err) {
return res.status(500).json({ error: err });
}
// assert: no error - process the result set
if (rows.length == 0) {
// no users found
res.status(400).json({ msg: "No users found" });
} else {
// process the user records
let users = [];
rows.forEach((row) => {
let user = {
uid: row.uid,
email: row.email,
role: row.role,
dateCreated: row.created_date,
password: row.password,
};
users.push(user);
});
if (users[0]) {
// Does given password hash match the database password hash?
bcrypt.compare(req.body.password, users[0].password, (err, result) => {
// Send back a token that contains the user's username
const token = jwt.encode({ email: req.body.email }, config.secret);
if (result == true) {
res.status(200).json({
msg: "user authenticated",
fname: users[0].fname,
lname: users[0].lname,
role: users[0].role,
token: token,
});
} else {
res.sendStatus(401);
}
});
}
}
});
});
router.post("/auth", cors(), (req, res) => {
try {
let user = jwt.decode(req.body.token, config.secret);
res.status(200).send(user);
} catch (err) {
res.sendStatus(401);
}
});
SignIn.js client/src/components. This is wrapped in a react.useEffect() arrow function, but again I don't believe the issue is here because this page remains unchanged from a working version.
const handleSubmit = (e) => {
e.preventDefault();
const credentials = { email, password };
// API call to login to account
// if successful, redirect to landing page
// if not, display error message
fetch(`http://${process.env.REACT_APP_API_URL}/api/users/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(credentials),
})
.then(async (res) => {
if (res.status == 200) {
return res.json();
} else {
throw new Error("Failed to Login!");
}
})
.then(async (res) => {
// Store token in cookie
setCookie("token", res.token, { path: "/${path}", maxAge: 3600 * 24 });
// Toggle state of sign in
toggleSignIn();
// Feedback
setFeedback(true);
setFeedbackObj({ message: "Signed in!", severity: "success" });
//redirect
history.push(`${process.env.PUBLIC_URL}/simulation`);
})
.catch(async (err) => {
// Feedback
setFeedback(true);
setFeedbackObj({ message: "Sign In Error", severity: "error" });
console.error(err);
});
};
If there are any other files that are of interest please let me know.
I've tried to mess with the proxy in package.json, but I don't think thats the answer because it was working previously. I've had a really difficult time finding others with similar issues or resources other than how to build a simple app with Express backend and React.js front end. This is not our issue because our application was working perfectly before this big switch. I believe the issue is stemming from require statements in our API and the running of JS functions in the API. I have no way to confirm this because in production (deployment), the errors are super uninformative, and in development, it runs perfectly fine.
I have been trying to solve this issue for a couple of weeks now, and I've made very little progress. If anyone has suggestions or tips on troubleshooting deployment, I would greatly appreciate it.
Thanks!

How to fix "TypeError: Cannot read property 'body' of undefined' in an expressjs REST API"

I'm setting up a REST API using Express, I ended up defining some endpoints and methods thats works, when suddenly appears an error:
"TypeError : Cannot read property 'body' of undefined"
I'm fairly new to JS and i'm trying to build a webapp using mongodb, express and react.
I've been following some guides (this one in particular because it also implements JWT : https://www.toptal.com/nodejs/secure-rest-api-in-nodejs)
I have managed to build all the users methods for basic CRUD operations and have exposed them. Everything works fine, then I tried to add the Auth process with middlewares, and the error happened.
I looked for the answer, most commonly the error was due to body-parser being called after the routes. But in my case, I call the auth route just before the user route which works fine.
Here is my git repo for more details :
https://github.com/pidanou/btb_api
const config = require("./config/env.config");
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
const authRouter = require("./routes/auth.routes");
const userRouter = require("./routes/users.routes");
app.use(function(req,res,next){
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Credentials', 'true');
res.header('Access-Control-Allow-Methods',
'GET,HEAD,PUT,PATCH,POST,DELETE');
res.header('Access-Control-Expose-Headers', 'Content-Length');
res.header('Access-Control-Allow-Headers', 'Accept, Authorization,
Content-Type, X-Requested-With, Range');
if (req.method === 'OPTIONS') {
return res.send(200);
} else {
return next();
}
});
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:true}));
authRouter.authRoutesConfig(app);
userRouter.userRoutesConfig(app);
app.listen(config.port, () => console.log(`Listening on port
${config.port}`));
Code for the authRoute:
const verifyUserMiddle = require("../middlewares/verify.user.middle");
const authController = require("../controllers/auth.controller");
exports.authRoutesConfig = function (app) {
app.post('/auth', [
verifyUserMiddle.hasAuthValidFields(),
verifyUserMiddle.isPasswordAndUserMatch(),
authController.login()
])
}
Code for the controller:
const jwtSecret = require('../config/env.config').jwt_secret,
jwt = require("jsonwebtoken");
const cyrpto = require("crypto");
exports.login = (req, res) => {
try {
let refreshId = req.body.userId + jwtSecret;
let salt = crypto.randomBytes(16).toString("base64");
let hash = cyrpto.createHmac("sha512",
salt).update(refreshId).digest("base64");
req.body.refreshKey = salt;
let token = jwt.sign(req.body, jwtSecret);
let b = new Buffer(hash);
let refresh_token = b.toString("base64");
res.status(201).send({accessToken: token, refresh_token:
refresh_token});
}catch(err){
res.status(500).send({errors: err});
}
}
Since the user part worked fine, I thought the auth would too, but it didn't.
It seems that the middleware is called before the body-parser.
The error happens first in the middleware.
It's not a body-parser issue here - it works fine. As you can see your exports.login = (req, res) => {...} has req and res parameters, but how are they being received?
With having parantheses() in your authController.login() you prevented that, and as long as they're present, your app will crash and give such error.
So change to this:
app.post('/auth', [
(your code),
authController.login
])
Without parentheses.

node, express - restrict access to downloadable file

I am using express as server for micro-services rest api. Endpoints are built from directory structure. There are few downloadable pdf files which are currently at client side. And it can be downloadable (with the href URL) even if user is not logged into the portal. So, I put all the pdf files to server.
Directory structure on server:
pdf files are inside docs directory. Please find below the code of server:
/* global __dirname */
import morgan from 'morgan';
import logger, { webStream } from './services/logger';
import { socket } from './services';
// set env variables before all else
import { GATEWAY_PORT, CORS_ORIGINS } from './config';
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser')();
const version = require('./services/utils').version();
const authentication = require('./services/authentication');
const utils = require('./services/utils');
// set up app and middleware
const app = express();
app.use(morgan('User::req[user-id] Correlation::req[x-correlation-id] Method::method URL::url Status::status :res[content-length] - :response-time ms', { stream: webStream }));
logger.info('Starting...');
app.use(cookieParser);
app.use(bodyParser.json({ limit: '50mb' }));
app.disable('x-powered-by');
// CORS headers to allow running client/server on different ports
app.use((req, res, next) => {
// Check if the origin is whitelisted in the env vars
const actual = req.headers.origin || '';
if (utils.matchCors(actual, CORS_ORIGINS.split(','))) {
res.set({ 'Access-Control-Allow-Origin': actual });
}
res.set({
// standard CORS headers
'Access-Control-Allow-Headers': 'Content-Type, Authorization, Accept, Accept-Language',
'Access-Control-Allow-Credentials': true,
'Access-Control-Allow-Methods': 'PATCH,POST,GET,DELETE',
// addresses security issues identified by automated pen testing
'X-Frame-Options': 'DENY',
'X-Content-Type-Options': 'nosniff',
'X-XSS-Protection': 1,
});
next();
});
// set the user property of the request object
app.use((req, res, next) => {
const token = req.cookies[authentication.cookieName];
if (!token) {
req.user = false;
} else {
req.user = authentication.decodeJWT(token);
authentication.setCookie(res, token, req.user);
}
utils.setCorrelationId(req.headers['x-correlation-id']);
req.correlationId = req.headers['x-correlation-id'];
next();
});
// helper function returning middleware to reject unauthorised users
function requiredRoles(roles, abcOnly) {
return function requireRolesHandler(req, res, next) {
if (
!req.user
|| (abcOnly && !req.user.isabc)
|| !authentication.hasRole(req.user, roles)) {
const error = new Error('UNAUTHORISED');
error.status = 403;
next(error);
} else {
next();
}
};
}
// Add the endpoints to express.
// Reversed to get literal routes before # capture groups.
utils.parseDirectory(`${__dirname}/rest`, [], true).reverse().forEach((endpoint) => {
const { auth, functions } = endpoint.handler;
if (auth) {
functions.unshift(requiredRoles(auth.roles, auth.abcOnly));
}
app[endpoint.method](
endpoint.url,
functions,
);
});
// setup server
const server = app.listen(GATEWAY_PORT, () => {
logger.info(`Allowed CORS: ${CORS_ORIGINS}`);
logger.info(`Started ${version.name} (${version.number}) listening on ${GATEWAY_PORT}`);
});
socket.createServer(server);
How do I serve pdf files from server to client only to authorized user when user clicks on link on a page ?
Have a route to download file e.g. GET /api/download?file=abc.pdf
Now in the middleware,
Check if the req.user exists or not.
Check if the user has sufficient rights to download the file or
not
If 1 and 2 satisfy, then serve the file
Code would look more or less like this:
app.get('/api/download', (req, res, next) => {
// Check if the request had valid token or not
if(!req.user) {
const error = new Error('UNAUTHORISED');
error.status = 403;
return next(error);
}
const { user } = req;
const { file } = req.query;
// If you want to have some additional logic wherein
// you want to restrict the download of the file,
// you can put that logic in this function
const isAllowed = canDownload(user, file);
if(isAllowed) {
return res.sendFile(path.join(__dirname, 'docs', path.sep, file));
}
const error = new Error('UNAUTHORISED');
error.status = 403;
return next(error);
})
You might need to require path, implement canDownload or solve no such file or directory errors because of __dirname usage. All of those are trivial. If you need help for those as well, let me know in the comments.
Here is the reference to response.sendFile()
And this might be helpful too.

NodeJS Express - Global Unique Request Id

Is it possible to define a unique request Id that is included in each log statement without handing the logger to each method/function call?
Technologies in use: NodeJS, Express, Winston
Edited
Finally, I have created a library that makes all the work.
https://github.com/davicente/express-logger-unique-req-id
It is a wrapper of Winston library, so you can use it the same way.
Let me know if it helps you
We had this same problem in several projects, and I couldn't finde any complete solution for this question. We are using same technologies (Node.js, Express.js and Winston for logs)
I found a solution to this using a couple of libraries and wrapping Winston library:
- node-uuid for creating unique identificators for each request
- continuation-local-storage for sharing this IDs among different modules without sending req object in all the calls.
First I need to create and set the unique identificator with each request. I do it in the middleware:
var uuid = require('node-uuid');
var createNamespace = require('continuation-local-storage').createNamespace;
var myRequest = createNamespace('my request');
// Run the context for each request. Assign a unique identifier to each request
app.use(function(req, res, next) {
myRequest.run(function() {
myRequest.set('reqId', uuid.v1());
next();
});
});
After that I had to wrap Winston library, recovering the id from the context and adding to the message of the log:
var winston = require('winston');
var getNamespace = require('continuation-local-storage').getNamespace;
// Wrap Winston logger to print reqId in each log
var formatMessage = function(message) {
var myRequest = getNamespace('my request');
message = myRequest && myRequest.get('reqId') ? message + " reqId: " + myRequest.get('reqId') : message;
return message;
};
var logger = {
log: function(level, message) {
winstonLogger.log(level, formatMessage(message));
},
error: function(message) {
winstonLogger.error(formatMessage(message));
},
warn: function(message) {
winstonLogger.warn(formatMessage(message));
},
verbose: function(message) {
winstonLogger.verbose(formatMessage(message));
},
info: function(message) {
winstonLogger.info(formatMessage(message));
},
debug: function(message) {
winstonLogger.debug(formatMessage(message));
},
silly: function(message) {
winstonLogger.silly(formatMessage(message));
}
};
module.exports = logger;
I think it was a little bit complex, so I decided to write it down in a post. You can get more information from there: Express.js: Logging info with global unique request ID – Node.js
I hope this helps with your problem.
This answer has a problem: the counter goes back to 0 every time the node process is restarted. Turns out there is fairly simple to work around. You simply add an express middleware that tags each request called with a UUID using the uuid package.
For uuid Pre-2.0
const uuid = require('uuid');
const app = express();
app.use(function (req, next) {
req.id = uuid.v4();
next();
});
For uuid 3.0+
const uuidv4 = require('uuid/v4');
const app = express();
app.use(function (req, next) {
req.id = uuidv4();
next();
});
At the very beginning of your request handling add something like the following (or put it in its own file):
var makeID = (function() {
var index = 0;
return function() {
return index++;
}
})();
app.use(function(req, res, next) {
req.id = makeID()
next()
})
This will give every request a unique (sequential) id. Do not allow people to identify themselves with it, only use it internally!
When logging in Winston, you can enter metadata, to be displayed after the message (in the form ${name}=${value}), which looks like this
app.use(function(req, res) {
winston.log('info', 'Test Log Message', { id: req.id });
res.end("Done.")
});
Hope this is helpful.

How do I make a simple form GET and POST using Express and Mongo?

I'm trying to follow this Express.js and MongoDB tutorial with the difference of making my index page have a form where you save an email address.
This is the main code so far:
emailprovider.js
var Db = require('mongodb').Db;
var Connection = require('mongodb').Connection;
var Server = require('mongodb').Server;
var BSON = require('mongodb').BSON;
var ObjectID = require('mongodb').ObjectID;
EmailProvider = function(host, port) {
this.db= new Db('email-test', new Server(host, port, {safe: false}, {auto_reconnect: true}, {}));
this.db.open(function(){});
};
EmailProvider.prototype.getCollection= function(callback) {
this.db.collection('emails', function(error, email_collection) {
if( error ) callback(error);
else callback(null, email_collection);
});
};
//save new email
EmailProvider.prototype.save = function(emails, callback) {
this.getCollection(function(error, email_collection) {
if( error ) callback(error)
else {
if( typeof(emails.address)=="undefined")
emails = [emails];
for( var i =0;i< emails.address;i++ ) {
email = emails[i];
email.created_at = new Date();
}
email_collection.insert(emails, function() {
callback(null, emails);
});
}
});
};
exports.EmailProvider = EmailProvider;
app.js
var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, http = require('http')
, path = require('path')
, EmailProvider = require('./emailprovider').EmailProvider;
var app = express();
app.get('/', routes.index);
app.get('/users', user.list);
//Routes
//get new email form
app.get('/email/new', function(req, res) {
res.render('email_new', {
title: 'Email Address'
});
});
//save new email
app.post('/email/new', function(req, res){
emailProvider.save({
address: req.param('name')
}, function( error, docs) {
res.redirect('/')
});
});
index.js
.form-area
form(method='post')
input(type='text', name='address', placeholder='Your email address')
input(type='submit', value='Send')
I really don't have a clue what's going on in the emailprovider. I kinda got the routes down but right now when I try to save a new Email I get a Cannot POST / 404 error message. What's the correct way to do this?
Thanks.
EDIT: removed extra commas in jade syntax for input attributes.
Your EmailProvider variable is a reference to function and not an object instance. This may cause two problems:
The this operator on EmailProvider.js might not refer to EmailProvider as you wish.
Every call to EmailProvider.save() will run the db.collection again which can cause performance issues, memory leak as well other issues.
You should create an object instance from EmailProvider as follow:
var app = express();
var emailProvider = new EmailProvider(<some host>, <some port>);
...
If this operator causes problem (such as it does not recognize EmailProvider methods) call the call function after the save function as follow:
//save new email
app.post('/email/new', function(req, res){
emailProvider.save({
address: req.param('name')
}, function( error, docs) {
res.redirect('/')
}).call(emailProvider);
});
Hope it will help.

Resources