Rest API - Is it safe to get password in JSON response - json

I'm writing a Rest API with Node.js and using JWTs.
I have the route below to authenticate users.
I'd like to ask, the user that is returned from the method User.findOne returns the correct password hence I'm able to check if its correct.
However is this safe to do? I did a console.log and it shows the password (albeit encrypted) but still feels unsafe as someone could surely view?
router.post('/authenticate', function(req, res) {
// find the user
User.findOne({
name: req.body.name
}, function(err, user) {
if (err) throw err;
if (!user) {
res.json({ success: false, message: 'Authentication failed. User not found.' });
} else if (user) {
// check if password matches
if (user.password != req.body.password) {
res.json({ success: false, message: 'Authentication failed. Wrong password.' });
} else {
// if user is found and password is right
// create a token
var token = jwt.sign(user, app.get('superSecret'), {
expiresInMinutes: 1440 // expires in 24 hours
});
// return the information including token as JSON
res.json({
success: true,
message: 'Enjoy your token!',
token: token
});
}
}
});
});

No.
Furthermore, passwords shouldn't be saved encrypted in the database, but hashed. The classical environment saves passwords for example as md5 (more common) or bcrypt (more secure) hash.
This ensures that even if your database gets stolen, no one will have the passwords of your users; there is no way to "decrypt" the hashes (not in a hundred million years).
When a user logs in you compare a hash of the entered password with the hash assigned with the user. You can use great modules like bcrypt-nodejs
EDIT
From a technical aspect it wouldn't be dangerous. When you start your server JavaScript compiles your code and executes the result in a V8 engine. There is no way to access whats ever returned by your database as long as the connection between node and MySQL is save.
One could possibly dump the servers memory and hope to find the right bits. But if someone gains the necessary permissions, you'r doomed either way.
I implemented an example for you, it's untested, but should show you how it's meant.
router.post('/register', function(req, res) {
bcrypt.hash(req.body.password, null, null, function(err, hash) {
if (!err) {
var newUser = new User({
name: req.body.name,
password: hash
});
newUser.save(); // ????
}
});
});
router.post('/authenticate', function(req, res) {
User.findOne({
name: req.body.name
}, function(err, user) {
var password = 'GP%Z!zvbk/9>Ss-R';
var passwordHash = '$2a$10$W.zZPCaNOuR152I4qENKH.8h7I6BPcfCYBJqHPNXbVaBz0XWVxnBm'; // bcrypt of string ')RZK&M(QX"k188cw'
if (user) {
password = req.body.password;
passwordHash = user.password;
}
bcrypt.compare(password, passwordHash, function(err, success) {
if (success) {
var token = jwt.sign(user, app.get('superSecret'), {
expiresInMinutes: 1440
});
res.json({
success: true,
message: 'Enjoy your token!',
token: token
});
}
else {
res.status(401).json({
success: false,
message: 'Authentication failed.'
});
}
});
});
});
Note: bcrypt uses random salt by default for each hash operation. This means, whenever you hash a given input, it will produce different hashes each time. The salt is then stored as part of the hash which can be verified then. Please check Wikipedia for further info.

Related

Bcrypt compare password stays invalid even is password is correct on login

I'm trying to compare a password from my database with the password on login.
req.body.wachtwoord is the password send to the API to check.
results.0.wachtwoord is the hashed password in the database.
It doesn't matter if I enter the right or wrong password on login, the error still says Password is invalid
Here is my code for login to compare the 2 passwords:
bcrypt.compare(req.body.wachtwoord, results[0].wachtwoord, function(err, res) {
if (err) {
logger.error(err);
res.status(err.status).json(err);
}
if (res) {
// Send JWT
logger.info("passwords matched, sending userinfo en valid token.");
const { password, ...userinfo } = results[0];
const payload = { docentID: userinfo.docentID, };
logger.debug(payload);
jwt.sign(
payload,
jwtSecretKey, { expiresIn: "25d" },
function(err, token) {
if (token) {
logger.info("User logged in, sending: ", userinfo);
res.status(200).json({
status: 200,
result: {...userinfo, token },
});
}
}
);
} else {
logger.info("Password invalid");
res.status(401).json({
status: 401,
message: "Password invalid.",
datetime: new Date().toISOString,
});
}
});
Here's my full repo if you want to take a look for an error.
The password is being hashed in user.controller.js and is compared to the database in auth.controller.js.
Thanks in advance.
You're comparing against results[0].wachtwoord, but further up in the code the property seems to be called password:
const { password, ...userinfo } = results[0];
^^^^^^^^
My password was being stored in the database at 50 characters. This is too short for the bycrypt hash. I changed the length to 60 and now my compare works.
There was nothing wrong with my code for comparing and hashing ;)

Where should data-validation happen using Express (and MySQL)?

I'm currently learning MySQL by creating an REST API using Express. I've opted for validating data on the server instead of the database. My question is, WHERE on the server should I do that?
Should I validate data (for example minimum and maximum length for the username)...
...using a middleware before the controller file?
...in the controller file, after reciving the request and before sending the data to the models file? (example file below)
...the models file before querying? (example file below)
...some completely other solution I haven't thought of?
./controllers/authController.js
const register = async (req, res) => {
const { username, email, password } = req.body;
**// Validating input data here?**
// TODO hash password
const activationToken = generateActivationToken(48);
const newUser = await User.create(
{ username, email, password, activationToken },
(err, result) => {
console.log(err);
if (err)
return res.status(400).json({
msg: err.message || "Some error has occured. Please try again.",
});
else res.json({ result });
}
);
};
./models/User.js
var db = require("../dbconnection");
// constructor for User object
const User = function (user) {
this.username = user.username;
this.email = user.email;
this.password = user.password;
this.activationToken = user.activationToken;
};
User.create = (newUser, result) => {
**// Validating input data here?**
db.query("INSERT INTO users SET ?", newUser, (err, res) => {
if (err) return result(err, null);
console.log("Created user.");
result(null, { id: res.insertId });
});
};
module.exports = User;
What's the usual/best practice way of doing this? If there isn't a best practice, how do YOU do it? (I validate data on the front-end too of course.) Do you know of any good example projects I could take a look at?
Thank you for your time!
In OOP there's a principle called as:
"The information Expert principle"
According to this principle we should assign responsibilities / methods inside the objects that contain the most information to fulfil the task
(It helps us create cohesive classes).
So, you should probably put the validation logic inside the User model.

MEAN Stack app - Can't set headers after they are sent

I'm basically trying to create a register page using MEAN and I want to check against the database (Mongo using Mongoose) if the user and email already exists, if one of them is true, send a res.json fail.
And if the username and email are not already in the database, continue to add the user.
I'm getting an error "Can't set headers after they are sent." On the Node console and I'm trying to figure out why.
Once User.getUsername() returns a user in the callback (if the username passed exists in the db), shouldn't it return the json and end the request there? Why is it continuing to the User.adduser() function and trying to set the header there too?
Any help is appreciated, thank you.
router.post('/register', (req, res, next) =>{
let newUser = new User({ // Collect body info
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: req.body.password
});
// Check if username is available
User.getUserByUsername(newUser.username, (err, user) => {
if(err) throw err;
if(user){
return res.json({success: false, msg: 'User already exists'});
// I want to end here if there's a user
}
});
//Continue to add user if getUserByUsername() returns false for user
// Add user
User.addUser(newUser, (err, user) => { // Add user
if(err){
return res.json({success: false, msg: 'Failed to register'});
} else {
return res.json({success: true, msg: 'You have been successfully registered!'});
}
});
});
The logic you have doesn't work for as expected because of how asynchronous methods work. The current logic is do User.getUserByUsername then do User.addUser and if either method calls back, handle it. I'm pretty sure you want, do User.getUserByUsername, wait for it's callback, then call User.addUser if necessary. Here's a crude implementation, you could use promises or define the methods outside of the logic to clean it up. Also, make sure you are calling res.end() at some point in your code.
router.post('/register', (req, res, next) =>{
let newUser = new User({ // Collect body info
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: req.body.password
});
// Check if username is available
User.getUserByUsername(newUser.username, (err, user) => {
if(err) throw err;
if(user){
return res.json({success: false, msg: 'User already exists'});
// I want to end here if there's a user
}else{
//Continue to add user if getUserByUsername() returns false for user
// Add user
User.addUser(newUser, (err, user) => { // Add user
if(err){
return res.json({success: false, msg: 'Failed to register'});
} else {
return res.json({success: true, msg: 'You have been successfully registered!'});
}
});
}
});
});

passport send error by json

I'm making an app with express + passport and angularJS; I want to be able to send any errors produced from passport (such as username taken or no email provided) by json so my angularJS app can receive these errors in a json response. More specifically right now I want to have a json response to my signup POST method that outputs any errors. I have tried to do this for myself and I've search all over the web and stack overflow I just cannot work this out!
Here is my users route file in express:
var express = require('express');
var router = express.Router();
var isAuthenticated = require('../config/isAuthenticated');
module.exports = function(passport){
router.get('/loggedin', function(req, res){
res.send(req.isAuthenticated() ? req.user : '0');
});
router.post('/signup', passport.authenticate('local-signup', {
successRedirect : '/',
failureRedirect : '/signup',
failureFlash: true
}));
router.post('/login', passport.authenticate('local-login'), function(req, res){
res.send(req.user);
});
router.post('/signout', function(req,res){
req.logout();
res.json({redirect: '/'});
});
router.get('/authtest', isAuthenticated, function(req, res){
res.render('authtest', {user: req.user});
});
return router;
};
This is my passport signup strategy:
passport.use('local-signup', new LocalStrategy({
usernameField : 'username',
passwordField : 'password',
passReqToCallback : true
},
function(req, username, password, done){
process.nextTick(function(){
User.findOne({'local.username' : username}, function(err, user){
if(err) return done(err);
if (user) { //username already exists
return done(null, false, {message: 'Username already exists'});
} else if(!req.body.email) { //no email address provided
return done(null, false, {message: 'You must provide an email address!'});
} else {
var newUser = new User();
newUser.local.username = username;
newUser.generateHash(password, function(err, hash){
if(err) return done(err);
newUser.local.password = hash;
});
newUser.email = req.body.email;
newUser.servers = [];
newUser.save(function(err){
if(err) throw err;
return done(null, newUser);
});
};
});
});
}
));
I know looking at my code right now it looks like I haven't tried to solve this myself at all but this is just my latest working code; I have been stuck at this for the past few days!
Any help would be greatly appreciated :)
According to the current code of passport this is probably achievable by passing custom callback to handle all results of authentiction yourself. This callback is given after options or instead of those.
passport( "local-signup", { ... }, callbackFn );
or
passport( "local-login", callbackFn );
This callback is used in all resulting situations of trying to authenticae. It is thus invoked on processing errors like this:
callbackFn( err )
If (all configured) authentications have failed it is called with
callbackFn( null, false, challenge(s), status(es) )
On successfully having authenticated user the callback is invoked like so:
callbackFn( null, user, infos )
with infos optionally provided by strategies.
Now comes the bottom-side: In either situation passport.authenticate() skips usual processing but instantly invokes provided callback to care for the rest. This includes processing of any options passed in call for passport.authenticate() like flashing messages, preparing session and request for containing authenticated user etc.
Since options given passport.authenticate() are never passed into callback there is actually no obvious reason to use both.
When I was stumbling over the very same problem (linking passport-service with angular-js POST request) I declined to consider use of callback a proper solution. This callback isn't documented. And it doesn't even look quite useful for it isn't passing req, res and next to pass any actual request in callback. Thus it makes very little sense to use it at all and I'd expect it to vanish soon or to change its behaviour quite much.
So the second approach was about trying to figure out why there is a problem in AngularJS. Passport is sending plain text Unauthorized in response with status code 401. AngularJS is trying to parse this as JSON and produces Syntax error. The text Unauthorized results from passprt ending response very simply by invoking
res.statusCode = 401;
res.end(http.STATUS_CODES[res.statusCode]);
Thus a proper workaround might try to replace
either text in http.STATUS_CODES though this is having impact on processing further requests and thus isn't preferrable
or res.end() by an overloaded method acting differently if res.statusCode is 401.
Due to affecting any current request, only, I tried the latter. Replaced res.end() might be used to send any text you want:
router.post('/login',
function(req, res, next) {
var _end = res.end;
res.end = function() {
if (res.statusCode === 401) {
return _end('{"status":"Unauthorized"}');
}
return _end.apply(this, arguments);
};
next();
},
passport.authenticate('local-login'),
function(req, res) {
res.send(req.user);
}
);
Alternatively the replaced method might add previously missing response header information on content type, for this was actually causing issues in AngularJS processing that response as JSON by default.
router.post('/login',
function(req, res, next) {
var _end = res.end;
res.end = function() {
if (res.statusCode === 401) {
res.set("Content-Type", "text/plain");
}
return _end.apply(this, arguments);
};
next();
},
passport.authenticate('local-login'),
function(req, res) {
res.send(req.user);
}
);
Finally, either approach is really just a workaround. I think passport is in the need for revising this annoying limitation.

Nodejs + Passport + MySQL

I'm trying to figure out how to use nodejs + Passport + MySQL. It seems as though just about every tutorial out there is using mongoDB and I don't want to do that. In fact some quick searches of this type will yield web pages like (http://nodejsrocks.blogspot.com/2012/04/nodejs-expressjs-mysql.html) and a youtube video that is a guy (https://www.youtube.com/watch?v=jGBbMVJx3h0) doing nothing but loging in, and who knows what he is really using, but the page has had 3K + views. I'd hope that some of the developers would look at that and say maybe there is a use for something like a comprehensive non MVC type of thing with MySQL. My reason for this is I am trying to get iOS and Android capabilities only and have no need for a large scaffolding overhead. Just the DB and server side scripting handling the queries and returning JSON objects to the phones.
So, that being said, can someone who has had real experience with this please help me out(And the rest of the world trying to do similar things without any in-depth tutorials, because we aren't using mongoDB and full blown scaffolding).
The tables I have set up for a 'TwitterStrategy' are users(id (PK), username, email, salt, password), and twitterusers(id (PK), name, screenname, location, description, url, img, token, tokensecret).
Here is the code I am trying to get going from a single main.js file. I know this is not best practices, and I plan to clean it up later, but for now, I would like to understand what I am missing and get things working. It would be extremely appreciated if someone can help, and I'm SURE others would find this very useful as well. Thanks.
var http = require('http'),
mysql = require('mysql'),
url = require('url'),
crypto = require('crypto'),
express = require('express'),
flash = require('connect-flash'),
passport = require('passport'),
TwitterStrategy = require('passport-twitter').Strategy;
var db = mysql.createConnection({
host : "****",
user : "****",
password : "****",
port : '****',
database : '****'
});
// Connect the connection to DB and throw error if exists
db.connect(function(err) {
if (err) {
console.error('Error connecting to db');
console.error(err);
return;
}
console.log('Database connected');
});
var TWITTER_CONSUMER_KEY = "****";
var TWITTER_CONSUMER_SECRET = "****";
passport.use(new TwitterStrategy({
consumerKey: TWITTER_CONSUMER_KEY,
consumerSecret: TWITTER_CONSUMER_SECRET,
callbackURL: 'http://127.0.0.1:3000/auth/twitter/callback'},
function(accessToken, refreshToken, profile, done) {
//db.query(SELECT ........ FROM ...... WHERE ........, function (err, user){
if (err) {
console.log(err);
}
if (!err && user != null){
done(null, result);
} else {
console.log(result);
}
})
});
}
));
passport.serializeUser(function(user, done) {
console.log('serializeUser: ' + user.id);
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
db.query('SELECT * FROM users WHERE id = ' + id, function(err, result) {
if (err){
console.log(err);
} else {
console.log(result);
}
if (!err) {
done(null, result);
} else {
done(err, null);
}
});
});
var app = express();
app.set(function(){
// app.set('views', __dirname + '/views'); // Definitely for some views which aren't being used here
// app.set('view engine', 'jade'); // Using jade for views, not used
// app.use(express.favicon()); // Not really sure this is important, should be web only
app.use(express.logger('dev')); // Again, not really sure this is important
app.use(express.bodyParser()); // Have no idea what this is used for
app.use(express.methodOverride()); // Same no Fn clue
app.use(express.cookieParser('what the F'));
app.use(express.session());
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
// app.use(app.router); // Here again we are defining our routes in main, so shouldn't need this.
// app.use(express.static(__dirname + '/public'));
});
var server = http.createServer(function(req, res) {
console.log('url: ' + req.url);
var params = url.parse(req.url, true)
var path = params.pathname;
if (path == '/signup') {
console.log("User signing up");
onSignUp(params, res);
} else if (path == '/signin') {
console.log("User signing in");
onSignIn(params, res);
} else if (path == '/auth/twitter'){
passport.authenticate('twitter'),
function(req, res){
console.log('Twitter User Created or Signed In');
}
}
});
//Keep server alive and listening to requests with DB connected also
server.listen(3000);
Am I missing another auth table? What is it that I need to put in the MySQL statement where the dots are so that I can find the user, and what parameters are being passed from the user request to get the query going, i.e. what is this oauth ID I have seen in tutorials that is getting passed from what seems to be the user to twitter for authorization? Also, what should I be expecting from this callback from Twitter? Anyway, I'll be glad to post all of this somewhere for everyone else to look at once I have a solution made so that all of us using MySQL and node don't get left out and have to search google to find something that seems as though it should be readily available, instead of copies of the same exact nodejs + mongoDB + express tutorial (with many that are out of date except for the scotch io, which looks very good if you wanna use mongo...might I add instances over at Amazon run about $279 estimated per month on the low end) that is floating around and being redistributed by nearly anyone with a "tutorial" out there. Thanks again.
Try wrapping strategy function under process.nextTick, e.g.,
passport.use(new TwitterStrategy({
consumerKey: TWITTER_CONSUMER_KEY,
consumerSecret: TWITTER_CONSUMER_SECRET,
callbackURL: 'http://127.0.0.1:3000/auth/twitter/callback'},
function(accessToken, refreshToken, profile, done) {
process.nextTick(function(){
// this is where you put logic to check the profile sent from twitter already in your DB or not,
// its totally up to you whether you keep a separate auth table for it or not
// NB: there will be some unique value in profile that can be used for next references
db.query(SELECT ........ FROM ...... WHERE ........, function (err, user){
if (err) {
console.log(err);
}
if (!err && user != null){
done(null, result);
} else {
console.log(result);
}
})
});
});
}
));
you also have to have a route for accepting the callback, e.g.,
app.get('/auth/twitter/callback', function(req, res, next) {
passport.authenticate('twitter',
{ },
function(err, user) {
// the result you send from the strategy function will be here
// do anything you like with the user send
}
)(req, res, next);
});
Hope it makes things clearer.