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
Related
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.
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.
So i am making a react project currently, and i have established connection to a MYSQL database with encryption. But i have no idea of how to make it possible to login afterwards.
app.post("/signup", (req, res) => {
const { email, firstname, lastname, password } = req.body;
const hashedPassword = encrypt(password);
db.query("INSERT INTO accounts (email, fName, lName, password, iv) VALUES (?, ?, ?, ?, ?)", [
email,
firstname,
lastname,
hashedPassword.password,
hashedPassword.iv
], (err, result)=>{
if(err) {
console.log(err);
}
else {
console.log("Success!");
}
}
);
});
This is where i store data inside the database.
Your query looks like you want to register a new user, since you insert new values inside your database. For a login, you'd just have to check whether the user info needed for the login is correct and exists in your database.
But either way, you should have some input fields where the user can login or register. After the user clicked the submit button you can execute a function which runs the sql query. After the query result was successful, you can proceed and show another view (a dashboard or whatever your app is about).
I am using koa-passport for authentication and credentials are stored on a mySQL server. Each username has a unique ID in the database table. I am using a very simple scheme where I just use IDs for both serialization and deserialization instead of having a whole user object.
For authentication, there is a stored procedure defined in the database which returns either null or the userID if found in the database for the credentials supplied (the stored procedure looks at both username and password so no need to handle on nodeJS. Please assume that the code snippet below directly gives us the values without formatting the results from the mySQL database for sake of simplicity)
const localStrategy = require('passport-local').Strategy;
passport.serializeUser((ctx, ID, done) => {
done(null, ID);
});
passport.deserializeUser(async (ctx, ID, done) => {
mySQLPool.getConnection(function (err, thisConnection) {
let sqlQuery = "select * from table where userID = " + ID + "";
thisConnection.query(sqlQuery, function (error, results) {
thisConnection.release();
console.log("De-serializing User");
done(error, results.userID);
})
})
});
passport.use(new localStrategy({
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true
}, (async function (ctx, username, password, done) {
mySQLPool.getConnection(function (err, thisConnection) {
let sqlQuery = "CALL " + leadingSQLStoredProc + username + "', '" + password + trailingSQLStoredProc;
console.log("Authenticating user....")
thisConnection.query(sqlQuery, function (error, results) {
thisConnection.release();
if (error)
return done(error);
if (results.userID !== null)
return done(null, results.userID);
else
return done(null, false);
})
})
})))
Is there a way to avoid accessing the database on each deserialization?
Yes, you can serialize and store entire user object instead of its id.
But I don't recommend you doing so because data in the session can become obsolete. It is much better to fetch it from the database on every request.
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).