i am trying to simulate login using Node.Js , JWT and mysql
i am always getting invalid user and pass, i started to wonder what i was not doing correctly.
My code:
app.post('/api/v1/user/login', async function(req,res){
var email = req.body.email;
var password = req.body.password;
var hashPass = await bcrypt.hashSync(password,12);
const bycryptPass = bcrypt.compareSync(password,hashPass);
dbConn.query('select * from xxxx_users where email =? and password =?',[email,bycryptPass],function(error,results,fields){
if(results.length > 0){
const token = jwt.sign({id:row[0].id},'the-super-strong-secrect',{ expiresIn: '1h' });
res.send({error: false, message: 'OK', token: token})
}else{
res.send({error: true, message: 'Invalid User or Pass'})
}
})
})
what am i not doing correctly? Why does it report that the login user and pass is always failed?
Compare hash would give you a boolean result based on the 2 values that you passed into it.
First, you have to get the user record based on the username and then check the password or pass hashed password to the query itself.
const hashPass = await bcrypt.hashSync(password,12);
//const bycryptPass = bcrypt.compareSync(password,hashPass);
dbConn.query('select * from xxxx_users where email =? and password =?',[email,hashPass],function(error,results,fields){
if(results.length > 0){
const token = jwt.sign({id:row[0].id},'the-super-strong-secrect',{ expiresIn: '1h' });
res.send({error: false, message: 'OK', token: token})
}else{
res.send({error: true, message: 'Invalid User or Pass'})
}
})
I prefer the following
const user = await getUserByUsername(loginRequest.userName);
if (user && compareHash(user.password, loginRequest.password)) {
//login success access
}
bcrypt will never produce the same hash for the same password. It's one of its design features.
Your general flow would work for older ways to hash passwords, like md5, sha256, but these are no longer recommended.
The general correct flow for implementing login works roughly like this:
Given that you have a username and password
Pull out the user record from the database based on the username alone (not the password)
Then use the compare function to see if the password the user supplied is comparable to the hash in the database.
It's impossible to select on the password hash, it will always be wrong.
Related
I used Bcryptjs to encrypt a password stored in mysql database. The Problem I have is the login porcess trying to compare the password in the Mysql Database to the login password.The Error message is what I get below.
(node:3616) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'password' of undefined
The enter code is below.
exports.login = async(req,res) => {
try{
//const email = req.body.email;
//const password = req.body.password;
const {email,password} = req.body;
if(!email || !password){
res.status(400).render('login',{message:'Both Email and Password are required!'});
}
db.query('SELECT * FROM users WHERE email=?', [email], async function(error,results){
console.log(results);
//the problem is the compare
if(!results || !(await bcrypt.compare(password,results[0].password))){
res.status(401).render('login',{message:'Email or Password is incorrect'});
}
});
}catch(err){
console.log(err);
}
}
Thanks for the help.
I am using node.js/express/sequelize lo log in a user. I'm testing making the requests in postman to see if it gets anything. I am testing a user and pw that I know are in the db but my findOne query in sequelize returns null even though the tested name is in the db. Here's the query in question:
let { userId, pw } = req.body
try{
const foundUser = await User.findOne({ where: { userId: userId } });
if (foundUser === null) {
return res.status(401).send({ auth: false, token: null,error: 'name not found' });
} else {
const passwordIsValid = bcrypt.compareSync(pw, foundUser.pw);
if (!passwordIsValid) return res.status(401).send({ auth: false, token: null,error: 'name not found' });
const token = jwt.sign({ id: foundUser.id}, config.secret, {
expiresIn: 86400 // expires in 24 hours
});
res.status(200).json({auth:true,token:token});
}
} catch (err) {
res.status(500).json(err);
}
}
my json
I have body-parser implemented for jsonenter image description here on index js and the routing works as the userId and pwd values make it to the findOne query. However it still says that there are no results after the query. Terminal runs the following (userId is 'slim')
Executing (default): SELECT id, username, userId, pw, createdAt, updatedAt FROM users AS users WHERE users.userId = 'slim';
'slim' is both in the Json and in the 'userId' section of the db
it turns out the issue was because there was more than one 'slim' in the userId section of my db, it was acting funny. When I entered a truly unique value for userId, it returned the token as expected. Maybe it seems obvious but it's important that that value called for authentication in unique.
I'm developing a MySql, Express, Angular, NodeJS application and I'm trying to wrap my head around signing up users, I can't find a propper source that provides to information I'm looking for.
I know how to create a user record with a username and password but only how to store the database as text and not hashed/salted.
I'm thinking I just need to find out how to created a hashed password when a user signs up. Then figure out how to place that hashed password in the database and then finally when a user wants to sign in compare the passwords. But it all feels a bit abstract and I can't find any information of passport does any of this.
we are using named strategies since we have one for login and one for signup
by default, if there was no name, it would just be called 'local'
passport.use('local-signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) {
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
connection.query("select * from users where email = '"+email+"'",function(err,rows){
console.log(rows);
console.log("above row object");
if (err)
return done(err);
if (rows.length) {
return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
} else {
// if there is no user with that email
// create the user
var newUserMysql = new Object();
newUserMysql.email = email;
newUserMysql.password = password; // use the generateHash function in our user model
var insertQuery = "INSERT INTO users ( email, password ) values ('" + email +"','"+ password +"')";
console.log(insertQuery);
connection.query(insertQuery,function(err,rows){
newUserMysql.id = rows.insertId;
return done(null, newUserMysql);
});
}
});
}));
For more details plz check this https://gist.github.com/manjeshpv/84446e6aa5b3689e8b84
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.
I am using node.js and passport and mysql for a userlogin.
I the main source is from https://github.com/manjeshpv/node-express-passport-mysql/issues
I want to add more columns in the table. I started with emailfield and change the code like below. I simply added the email variable at the needed places I think. I cannot find the bug where its crashing. without modifying anything, the code does work.
passport.js:
passport.use(
'local-signup',
new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'username',
passwordField : 'password',
//emailField : 'email',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, username, password, email, done) {
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
connection.query("SELECT * FROM users WHERE username = ?",[username], function(err, rows) {
if (err)
log.info(err);
//return done(err);
if (rows.length) {
return done(null, false, req.flash('signupMessage', 'That username is already taken.'));
} else {
// if there is no user with that username
// create the user
var newUserMysql = {
username: username,
email: email,
password: bcrypt.hashSync(password, null, null) // use the generateHash function in our user model
};
var insertQuery = "INSERT INTO users ( username, password, email ) values (?,?,?)";
connection.query(insertQuery,[newUserMysql.username, newUserMysql.password, newUserMysql.email],function(err, rows) {
newUserMysql.id = rows.insertId;
return done(null, newUserMysql);
});
}
});
})
);
and here the log:
The magic happens on port 8080
GET /signup 200 20ms - 1.21kb
D:\node-express-passport-mysql\node_modules\mysql\lib\protocol\Parser.js:82
throw err;
^
TypeError: undefined is not a function
at Object.SqlString.escape (D:\node-express-passport-mysql\node_modules\mysq
l\lib\protocol\SqlString.js:46:13)
at D:\node-express-passport-mysql\node_modules\mysql\lib\protocol\SqlString.
js:80:19
at String.replace (native)
at Object.SqlString.format (D:\node-express-passport-mysql\node_modules\mysq
l\lib\protocol\SqlString.js:71:14)
at Connection.format (D:\node-express-passport-mysql\node_modules\mysql\lib\
Connection.js:263:20)
at Connection.query (D:\node-express-passport-mysql\node_modules\mysql\lib\C
onnection.js:196:22)
at Query._callback (D:\node-express-passport-mysql\config\passport.js:71:32)
at Query.Sequence.end (D:\node-express-passport-mysql\node_modules\mysql\lib
\protocol\sequences\Sequence.js:96:24)
at Query._handleFinalResultPacket (D:\node-express-passport-mysql\node_modul
es\mysql\lib\protocol\sequences\Query.js:144:8)
at Query.EofPacket (D:\node-express-passport-mysql\node_modules\mysql\lib\pr
otocol\sequences\Query.js:128:8)
28 Jun 21:03:58 - [nodemon] app crashed - waiting for file changes before starti
ng...
This looks to be the problem:
function(req, username, password, email, done) {
You added an extra argument email which shouldn't be there. Since it clobbers the done callback, when your code tries to call it it will cause an "undefined is not a function" error.
If you're passing an extra email property, you can access it through req.body.email (assuming that you're using a POST route to log in).