Node.js Passport strategy login with either Email or Username - javascript

I am using Passport on my node.js app and I am currently using an Username to login.
On my user register page, I have allow the user to register their unique username and email.
I want a login page with "Sign in Using username/email:" ________
Where the script can detect if there is a "#" in the field and look up the email instead of username.
I have tried for a few hours with no avail.
here is my passport.js
var mongoose = require('mongoose')
var LocalStrategy = require('passport-local').Strategy
var User = mongoose.model('User');
module.exports = function(passport, config){
passport.serializeUser(function(user, done){
done(null, user.id);
})
passport.deserializeUser(function(id, done) {
User.findOne({ _id: id }, function (err, user) {
done(err, user);
});
});
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password'
}, function(username, password, done) {
User.isValidUserPassword(username, password, done);
}));
}
EDIT: below is the user.js as requested
var mongoose = require('mongoose');
var hash = require('../util/hash.js');
UserSchema = mongoose.Schema({
username: String,
email: String,
salt: String,
hash: String
})
UserSchema.statics.signup = function(username, email, password, done){
var User = this;
hash(password, function(err, salt, hash){
if(err) throw err;
// if (err) return done(err);
User.create({
username : username,
email: email,
salt : salt,
hash : hash
}, function(err, user){
if(err) throw err;
// if (err) return done(err);
done(null, user);
});
});
}
UserSchema.statics.isValidUserPassword = function(username, password, done) {
this.findOne({username : username}, function(err, user){
// if(err) throw err;
if(err) return done(err);
if(!user) return done(null, false, { message : 'Incorrect username.' });
hash(password, user.salt, function(err, hash){
if(err) return done(err);
if(hash == user.hash) return done(null, user);
done(null, false, {
message : 'Incorrect password'
});
});
});
};
var User = mongoose.model("User", UserSchema);
module.exports = User;

Ok, you should have something like this in your Mongoose model:
UserSchema.statics.isValidUserPassword = function(username, password, done) {
var criteria = (username.indexOf('#') === -1) ? {username: username} : {email: username};
this.findOne(criteria, function(err, user){
// All the same...
});
};

its a mongoose thing rather than a passport thing, and if you can try out this, using bredikhin's answer ^_^ :
var criteria = {$or: [{username: username}, {email: username}, {mobile: username}, {anything: username}]};
The key is to find a user through a query from mongodb!!!
In this way, you can have whatever query you want to find a user.

For sign ins with username OR email, you could also use passport-local-mongoose's findByUsername option modify the queryParameters
// add passport functions
// schema.plugin(passportLocalMongoose);
schema.plugin(passportLocalMongoose, {
// allow sign in with username OR email
findByUsername: function(model, queryParameters) {
// start
// // queryParameters => { '$or' : [ { 'username' : 'searchString' } ] }
// iterate through queryParameters
for( let param of queryParameters.$or ){
// if there is a username
if( typeof param == "object" && param.hasOwnProperty("username") ){
// add it as an email parameter
queryParameters.$or.push( { email : param.username } );
}
}
// expected outcome
// queryParameters => { '$or' : [ { 'username' : 'searchString' }, { 'email' : 'searchString' } ] }
return model.findOne(queryParameters);
}
});

its very simple you have to redefine the passport strategy your self, just like in the code below,
serialize with username
passport.serializeUser(function(user, done) {
done(null, user.username);});
deserialize with username
passport.deserializeUser(function(username, done) {
User.findOne({username:username},function(err, user){
done(err,user);
}); });
Passport Strategy
//passport strategy
passport.use(new LocalStrategy(function(username, password, done) {
console.log(username.includes("#"));
User.findOne((username.includes("#"))?{email:username}:{username:username}, function(err, user) {
if (err) {return done(err); }
if (!user) {console.log("i am ERROR"); return done(null, false, { message: 'Incorrect username.' });}
if (user.password===password) {return done(null, user); }
return done(null, false);
});
}
));
NOTE: Here user.password===password means that the password in database is stored as plaintext.. And you have to insert password manually to database such as password : req.body.password also you have to apply encryption and decryption user self before adding or in comparing.

Or you just make a middle-ware to handle the situation like so:
const User = require("../models/user");
var middlewareObj = {};
middlewareObj.checkUsername = function (req, res, next) {
if (req.body.username.indexOf('#') !== -1) {
User.findOne({ email: req.body.username }, (err, foundUser) => {
if (err || !foundUser) {
req.flash('error', 'Please check your username or password');
return res.redirect('/login');
} else {
req.body.username = foundUser.username;
next();
}
});
} else {
next();
}
}
module.exports = middlewareObj;
And simply add it to the login route:
app.post('/login', middleware.checkUsername, function (req, res, next) {
//Login logic goes here
}

Related

bcryptjs compare function returns false when passwords contain numbers

I have used bcryptjs to hash my passwords and the registration of the user takes place fine.
But when I try to login, the bcrypt.compare function returns a false promise if the passwords contain numbers or other characters.
Everything works fine if the password is just letters.
I have searched everywhere but could not find a solution for this error?
Here are my files :
users.js (MODEL)
var mongoose = require("mongoose");
var bcrypt = require("bcryptjs");
var userSchema = mongoose.Schema({
name: String,
email: String,
password: String,
products: [{
type: mongoose.Schema.Types.ObjectID,
ref: "Product"
}],
username: String
});
var User = module.exports = mongoose.model("User", userSchema);
module.exports.createUser = function(newUser, callback) {
bcrypt.genSalt(5,function(err,salt){
bcrypt.hash(newUser.password,salt,function(err,hash){
newUser.password = hash;
newUser.save(callback);
});
});
}
module.exports.comparePassword = function(candidatePassword, hash, callback){
bcrypt.compare(candidatePassword, hash, function(err, isMatch) {
if(err) throw err;
console.log(isMatch);
callback(null, isMatch);
});
}
module.exports.getUserByUsername = function(username, callback){
var query = {username: username};
User.findOne(query, callback);
}
app.js
app.use(passport.initialize());
app.use(passport.session());
var LocalStrategy = require("passport-local").Strategy;
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({username:username}, function(err, user){
if(err) throw err;
if(!user) {
return done(null,false, {message: 'Unknown user'});
}
User.comparePassword(password,user.password,function(err, isMatch){
if(err) {
console.log("ERR1");
return done(err);
}
if(isMatch) {
console.log("MATCH");
return done(null,user);
} else {
console.log("NOT VALID");
return done(null, false, {message: 'Invalid password'});
}
});
});
}
));
passport.serializeUser(function(user,done){
done(null,user.id);
});
passport.deserializeUser(function(id,done){
User.getUserById(id, function(err,user){
done(err,user);
})
})
users.js (ROUTE)
router.post("/login", passport.authenticate('local', function (error, user, info){
if(error) {
console.error(error);
console.log('Failed login:');
}
if (user === false) {
console.log("user not found");
} else {
// handle successful login ...
console.log("logged in");
}
}), function (req, res) {
res.send(req.user);
});
router.post("/signup", function (req, res) {
console.log(req.body);
var password = req.body.password;
var password2 = req.body.password2;
if(password == password2) {
var newUser = new User ({
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: req.body.username
});
User.createUser(newUser, function(err, user){
if(err)
throw err;
console.log(user);
res.send(user).end();
});
} else {
res.status(500).send("{errors: \"Passwords don't match\"}").end()
}
});
Whenever I enter a password that contains numbers, I get
false
NOT VALID
user not found
I'm sorry if I have done or not done something extremely simple. This is my first time using bcryptjs.
All answers are appreciated!
In the users.js file at the comparePassword function inside the compare method of bcrypt you are throwing the error instead of passing it to the callback. Maybe if you make this change:
module.exports.comparePassword = function(candidatePassword, hash, callback){
bcrypt.compare(candidatePassword, hash, function(err, isMatch) {
console.log(isMatch);
callback(err, isMatch);
});
}
You will see what kind of error it's generated by the case when you introduce numbers in your passwords.

How to handle Express Sessions?

I'm trying to deal with authentication sessions in Node.js, Express, Passport app.
I made lines of code to use express-session and it still can't auth even when I register new user.
Here is strategy.
// Local Strategy
passport.use(new LocalStrategy({ usernameField: 'email' }, function(username, password, done){
User.findOne({ 'email': username }, function(err, user){
if(err) throw err;
if(!user){
return done(null, false, {type: "danger", message: 'No user found'});
}
// Match Password
bcrypt.compare(password, user.password, function(err, isMatch){
if(err) throw err;
if(isMatch){
return done(null, user);
} else {
return done(null, false, {type: "danger", message: 'Wrong password'});
}
});
});
}));
Here are serializers.
passport.serializeUser(function(user, done) {
console.log(user.id);
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
console.log(user.id);
done(err, user);
});
});
Here is login and register route.
// Login page and form
router.get('/login', function(req, res) {
res.render('login');
});
router.post('/login', passport.authenticate('local',
{successRedirect: '/chat',
failureRedirect: '/login'}));
// Register page and form
router.get('/register', function(req, res) {
let errors = [];
res.render('register', { 'errors': '' });
});
router.post('/register', [
check('name').notEmpty().withMessage('Name field is empty'),
check('surname').notEmpty().withMessage('Surname field is empty'),
check('email').notEmpty().withMessage('E-mail is empty'),
check('password').notEmpty().withMessage('Password field is empty'),
check('password_confirm').notEmpty().withMessage('Password confirmation field is empty'),
check("password", "Passwords don't match")
.custom((value,{req}) => {
if (value !== req.body.password_confirm) {
throw new Error("Passwords don't match");
} else {
return value;
}
}),
], function(req, res) {
const { name, surname, email, password } = req.body;
let errors = validationResult(req);
console.log(errors.errors);
if(!errors){
res.render('register', { 'errors': errors.errors });
console.log('ebebe');
} else {
console.log('oooo');
let NewUser = new User ({
name, surname, email, password
});
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(password, salt, function(err, hash) {
NewUser.password = hash;
NewUser.save();
});
});
res.redirect('/chat');
}
});
Here is protected route.
router.get('/chat', (req, res) => {
if(req.isAuthenticated()) {
res.send('definitely secure page');
console.log(req.user);
console.log(req.isAuthenticated());
} else {
res.send('ebebe');
console.log(req.user);
console.log(req.isAuthenticated());
}
});
How to make it work properly and what am I doing wrong?
Here is a way to do it. You can use something like jsonwebtoken in combination with express-session and write a middleware function to check if the token is valid and use it to protect the routes that you want to protect. Here are some snippets of code that I hope will help guide you in the right direction.
First you can write a function like this in your UserSchema so you can use it later to generate a jwt token when the user logs in
var jwt = require('jsonwebtoken');
UserSchema.methods.generateJWT = function() {
var today = new Date();
var exp = new Date(today);
exp.setDate(today.getDate() + 60);
return jwt.sign({
id: this._id,
username: this.username,
exp: parseInt(exp.getTime() / 1000),
}, secret);
};
then in the login route you can use it to generate a token.
router.post('/login', passport.authenticate('local',
failureRedirect: '/login'}), function(req, res) {
req.user.token = user.generateJWT();
req.session.token = req.user.token;
res.redirect('/dashboard')
});
and then you can write the middleware
function auth(req, res, next) {
//console.log(req.session.token)
if (req.session.token) {
const token = req.session.token
let decoded = jwt.verify(token, secret)
console.log(decoded)
User.findById(decoded.id, function(err, user) {
if (err || !user) {
return res.redirect('/')
}
//console.log(user)
res.locals.user = user
req.user = user
next()
})
} else {
return res.redirect('/')
}
}
and then you can protect your routes with it
router.get('/protected', auth, function(req, res) {
})

Bcrypt not encrypting the password in Node JS

I am trying to make a user account system in node and am using bcrypt for hashing passwords. My syntax seems to be correct, and there is no error thrown in the console, however still the passwords aren't getting encrypted when stored in the database(mongo db).
This is my user.js file in models:
var mongoose = require('mongoose');
var bcrypt = require('bcrypt');
mongoose.connect('mongodb://localhost/nodeauth');
var db = mongoose.connection;
// User
var UserSchema = mongoose.Schema({
username: {
type: String,
index: true
},
password: {
type: String,
required: true,
bcrypt: true
},
email: {
type: String
},
name: {
type: String
},
profileImage: {
type: String
}
});
var User = module.exports = mongoose.model('User', UserSchema);
module.exports.comparePassword = function(candidatePassword, hash, callback) {
bcrypt.compare(candidatePassword, hash, function(err, isMatch) {
if(err) return callback(err);
callback(null, isMatch);
});
}
module.exports.getUserById = function(id, callback) {
User.findById(id, callback);
}
module.exports.getUserByUsername = function(username, callback) {
var query = {username: username};
User.findOne(query, function(err, user) {
callback(err, user);
});
}
module.exports.createUser = function(newUser, callback) {
bcrypt.hash(newUser.password, 10, function(err, hash) {
if(err) throw err;
// Set hashed password
newUser.password = hash;
// Create user
newUser.save(callback);
});
}
And this is the relevant part of users.js file in routes which is used in the login section to check whether the username and password match(which is also not working):
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.getUserById(id, function(err, user) {
done(err, user);
});
});
passport.use(new LocalStrategy(function(username, password, done) {
User.getUserByUsername(username, function(err, user) {
if(err) throw err;
if(!user) {
console.log('Unknown user');
return done(null, false, {message: 'Unknown User'})
}
User.comparePassword(password, user.password, function(err, isMatch) {
if(err) throw err;
if(isMatch) {
return done(null, user);
} else {
console.log('Invalid Password');
return done(null, false, {message: 'Invalid Password'});
}
});
});
}));
router.post('/login', passport.authenticate('local', {successRedirect: '/', failureRedirect:'/users/login', failureFlash:'Invalid username or password'}), function(req, res) {
console.log('Authentication Successful');
req.flash('success', 'You are logged in');
res.redirect('/');
});
module.exports = router;
I am not able to understand why the passwords aren't getting encrypted, and even then, why is comparePassword always returning a failure, thus making authentication fail every time as well.
Is there anything I am missing?

Passport: Allow sign up with name and email address? (Local Strategy)

Is there any way to allow a user to register on the local strategy with his password, email and name?
Every example I could find online only use name/password or email/password.
I also searched through the the whole passport documentation, but that documentation isn't helpful at all. It's just one bloated site full of examples.
I just need an list of functions, classes and variables passport uses with explanations what they and every parameter of them do. Every good library has something like that, why can't I find it for passport?
Here are the key parts of my code:
passport.use('local-signup', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
//are there other options?
//emailField did not seem to do anything
passReqToCallback: true // allows us to pass in the req from our route (lets us check if a user is logged in or not)
},
function(req, email, password, done) {
//check if email not already in database
//create new user using "email" and "password"
//I want an additional parameter here "name"
}));
So is passport really that limited? There has to be a way to do this, right?
You can be a little confused but passport doesn't implement signup methods. It's just authorisation library. So you must handle that use-case on your own.
First of all, create route that will be responsible for sign-up and your checks:
signup: function (req, res) {
User
.findOne({
or: [{username: req.param('username')}, {email: req.param('email')}]
})
.then(function(user) {
if (user) return {message: 'User already exists'};
return User.create(req.allParams());
})
.then(res.ok)
.catch(res.negotiate);
}
The example above is based on Sails framework, but you can fit it with no problems to your own case.
Next step is include passport local strategy.
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var LOCAL_STRATEGY_CONFIG = {
usernameField: 'email',
passwordField: 'password',
session: false,
passReqToCallback: true
};
function _onLocalStrategyAuth(req, email, password, next) {
User
.findOne(or: [{email: email}, {username: email}])
.then(function (user) {
if (!user) return next(null, null, {
code: 'E_USER_NOT_FOUND',
message: email + ' is not found',
status: 401
});
if (!HashService.bcrypt.compareSync(password, user.password)) return next(null, null, {
code: 'E_WRONG_PASSWORD',
message: 'Password is wrong',
status: 401
});
return next(null, user, {});
})
.catch(next);
}
passport.use(new LocalStrategy(LOCAL_STRATEGY_CONFIG), _onLocalStrategyAuth));
We have only signin task now. It's simple.
signin: function(req, res) {
passport.authenticate('local', function(error, user, info) {
if (error || !user) return res.negotiate(Object.assign(error, info));
return res.ok(user);
})(req, res);
}
This way is more suitable for passport and works great for me.
Say you have this
app.post('/login', urlencodedParser,
// so, user has been to /loginpage and clicked submit.
// /loginpage has a post form that goes to "/login".
// hence you arrive here.
passport.authenticate('my-simple-login-strategy', {
failureRedirect: '/loginagain'
}),
function(req, res) {
console.log("you are in ............")
res.redirect('/stuff');
});
Note that the .authenticate has an explicit tag.
The tags is 'my-simple-login-strategy'
That means you have this ...
passport.use(
'my-simple-login-strategy',
// !!!!!!!!!!!!!note!!!!!!!!!!, the DEFAULT there (if you have nothing)
// is 'local'. A good example of defaults being silly :/
new Strategy(
STRAT_CONFIG,
function(email, password, cb) {
// must return cb(null, false) or cb(null, the_user_struct) or cb(err)
db.findUserByEmailPass(email, password, function(err, userFoundByDB) {
if (err) { return cb(err); }
if (!userFoundByDB) { return cb(null, false); }
console.log('... ' + JSON.stringify(userFoundByDB) )
return cb(null, userFoundByDB)
})
}
)
)
!!! !!! NOTE THAT 'local' IS JUST THE DEFAULT TAG NAME !!! !!!
In passport.use, we always put in an explicit tag. It is much clearer if you do so. Put in an explicit tag in the strategy and in the app.post when you use the strategy.
So that's my-simple-login-strategy.
What is the actual db.findUserByEmailPass sql function?
We'll come back to that!
So we have my-simple-login-strategy
Next ...... we need my-simple-createaccount-strategy
Note that we are still sneakily using passport.authenticate:
So:
the strategy my-simple-createaccount-strategy will actually make an account.
However .............
you should still return a struct.
Note that my-simple-login-strategy has to return a struct.
So, my-simple-createaccount-strategy also has to return a struct - in exactly the same way.
app.post('/createaccount', urlencodedParser,
// so, user has been to /createanaccountform and clicked submit,
// that sends a post to /createaccount. So we are here:
passport.authenticate('my-simple-createaccount-strategy', {
failureRedirect: '/loginagain'
}),
function(req, res) {
console.log("you are in ............")
res.redirect('/stuff');
});
And here's the strategy ..........
passport.use(
'my-simple-createaccount-strategy',
new Strategy(
STRAT_CONFIG,
function(email, password, cb) {
// return cb(null, false), or cb(null, the_user_struct) or cb(err)
db.simpleCreate(email, password, function(err, trueOrFalse) {
if (err) { return cb(err); }
if (!trueOrFalse) { return cb(null, false); }
return cb(null, trueOrFalse)
})
}
)
)
The strategy is pretty much the same. But the db call is different.
So now let's look at the db calls.
Let's look at the db calls!
The ordinary db call for the ordinary strategy is going to look like this:
exports.findUserByEmailPass = function(email, password, cb) {
// return the struct or false via the callback
dc.query(
'select * from users where email = ? and password = ?',
[email, password],
(error, users, fields) => {
if (error) { throw error } // or something like cb(new Error('blah'));
cb(null, (users.length == 1) ? users[0] : false)
})
}
So that's exports.findUserByEmailPass, which is used by my-simple-login-strategy.
But what about exports.simpleCreate for my-simple-createaccount-strategy?
A simple toy version would
check if the username exists already - return false at this point if it does exist already, then
create it, and then
actually just return the record again.
Recall that (3) is just like in the ordinary "find" call.
Remember ... the strategy my-simple-createaccount-strategy will actually make an account. But you should still return a struct in the same way as your ordinary authenticate strategy, my-simple-login-strategy.
So exports.simpleCreate is a simple chain of three calls:
exports.simpleCreate = function(email, password, cb) {
// check if exists; insert; re-select and return it
dc.query(
'select * from users where email = ?', [email],
(error, users, fields) => {
if (error) { throw error } // or something like cb(new Error('blah'));
if (users.length > 0) {
return cb(null, false)
}
else {
return partTwo(email, password, cb)
}
})
}
partTwo = function(email, password, cb) {
dc.query(
'insert into users (email, password) values (?, ?)', [email, password],
(error, users, fields) => {
if (error) { throw error } // or something like cb(new Error('blah'));
partThree(email, password, cb)
})
}
partThree = function(email, password, cb) {
dc.query(
'select * from users where email = ? and password = ?', [email, password],
(error, users, fields) => {
if (error) { throw error } // or something like cb(new Error('blah'));
cb(null, (users.length == 1) ? users[0] : false)
})
}
And that all works.
But note that
passport has nothing to do with account creation!
In fact, you do not have to use a strategy at all.
In app.post('/createaccount' you can, if you wish, do nothing with passport.authenticate ... don't even mention it in the code. Don't use authenticate at all. Just go ahead and do the sql process of inserting a new user, right there in app.post.
However, if you "trickily" use a passport strategy - my-simple-createaccount-strategy in the example - you have the bonus that the user is then immediately logged-in with a session and everything works in the same pattern as the login post. Cool.
Here is what worked for me, the solution is based on a mongoose based odm, the first part is the passport related part, I also attached the user part from odm to who how the encryption of the password is done.
If I understood your question, you want the user to type either his email or password. In this case modify the search to try both, that is, match the provided user identifier (in your call to findOne(...) with either the username or password.
Note that I use bcrypt to avoid storing clear passwords, that's why there is a customized compare method for testing passwords. Also note 'hints' of using google auth as well, My system enabled both, if it is relevant, please lemme know and I can add the required code.
------------ Auth part (just relevant snippets) -----------
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy;
passport.serializeUser(function(user, done) {
// the values returned here will be used to deserializeUser
// this can be use for further logins
done(null, {username: user.username, _id: user.id, role: user.role});
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
passport.use(new LocalStrategy(function(username, password, done){
odm.User.findOne({username: username, authType: 'direct'}, function(err, user){
if(err){
return done(err, false);
}
if(!user){
return done(null, false);
}
if(user.role === 'new'){
console.log('can not use new user!');
return done('user not activated yet, please contact admin', false);
}
user.comparePassword(password,function(err, isMatch){
if(err){
return done(err, false);
}
if(isMatch){
return done(null, user);//{username: username});
}
return done(null, false);
});
});
}));
app.post('/login', function(req, res, next){
passport.authenticate('local', {
failureRedirect: '/logout?status=login failed'
}, function(err, user, info){
if(err){
return next(err);
}
if(!user){
return res.redirect('/login');
}
req.logIn(user, function(err){
if (req.body.rememberme) {
req.session.cookie.maxAge = 30*24*60*60*1000 ;//Rememeber 'me' for 30 days
} else {
req.session.cookie.expires = false;
}
var redirect = req.param('redirect') || '/index';
res.redirect(redirect);
});
}
)(req, res, next);
}
);
app.post('/register',function(req, res){
var user = new odm.User({username: req.body.username, password: req.body.password, email: req.body.email, authType: 'direct'});
user.save(function(err, user){
if(err){
console.log('registration err: ' , err);
} else {
res.redirect('/list');
}
});
});
--- user/odm, relevant parts ----------------
var bcrypt = require('bcrypt-nodejs');
// --------------------- User ------------------------------------------ //
var userSchema = new Schema({
name: String,
email: String,
username: {type: String, required: true, unique: true},
password: String,
role: {type: String, required: true, enum: ['new', 'admin', 'user'], default: 'new'},
authType: {type: String, enum: ['google', 'direct'], required: true}
});
userSchema.pre('save', function (next) {
var user = this;
if (!user.isModified('password')) return next();
console.log('making hash...........');
bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
if (err) return next(err);
bcrypt.hash(user.password, salt, null, function (err, hash) {
if (err) return next(err);
user.password = hash;
next();
});
});
});
userSchema.methods.comparePassword = function (candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function (err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
var localStrategy = require('passport-local').Strategy;
var User = require('../public/models/user');
module.exports = function(passport){
passport.serializeUser(function(user, done){
done(null, user.id);
});
passport.deserializeUser(function(id, done){
User.findById(id, function(err, user){
done(err, user);
});
});
passport.use('local-signup', new localStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
function(req, email, password, done){
process.nextTick(function(){
User.findOne({'local.enroll': email}, function(err, user){
if(err)
return done(err);
if(user){
return done(null, false, req.flash('signupmessage', 'The email already taken'));
} else{
var newUser = new User();
newUser.local.enroll = email;
newUser.local.password = newUser.generateHash(password);
newUser.save(function(err){
if(err)
throw err
return done(null, newUser);
});
}
});
});
}));
passport.use('local-login', new localStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
function(req, email, password, done){
process.nextTick(function(){
User.findOne({'local.enroll': email}, function(err, user){
if(err)
return done(err);
if(!user){
return done(null, false, req.flash('loginmessage', 'No user found'));
}
if(!user.validPassword(password)){
return done(null, false, req.flash('loginmessage', 'Invalid password'));
}
return done(null, user);
});
});
}));
}
This has actually nothing to do with passport and is pretty simple, assuming you are using body-parser. Make sure you have an input field in your form with the attribute name="name" where you register the user's name like:
<div class="form-group">
<label for="signup-name">Name</label>
<input type="text" placeholder="Name" name="name">
</div>
In your routing, you can access this field with req.body.name:
passport.use('local-signup', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
//are there other options?
//emailField did not seem to do anything
passReqToCallback: true
},
function(req, email, password, done) {
//check if email not already in database
//create new user using "email" and "password"
//I want an additional parameter here "name"
user.email = email;
user.password = password; // Do some hashing before storing
user.name = req.body.name;
}));
So you can add as many form input fields as you want, access them by the value of the name attribute. A Second example would be:
<input type="text" placeholder="City" name="city">
<input type="text" placeholder="Country" name="country">
// Access them by
user.city = req.body.city;
user.country = req.body.country;
UserModel.find({email: req.body.email}, function(err, user){
if(err){
res.redirect('/your sign up page');
} else {
if(user.length > 0){
res.redirect('/again your sign up page');
} else{
//YOUR REGISTRATION CODES HERE
}
}
})
In strategy options set the passReqToCallback:true and then add req as parameter into your callback function. Finally, read the extra information from req.body object for example req.body.firstName
const signup = new Strategy({
usernameField: "username",
passwordField: "password",
passReqToCallback:true
}, async (req, username, password, done) => {
try {
const user = User.create();
user.username = username;
user.password = password;
user.firstName = req.body.firstName;
user.lastName = req.body.lastName
await user.save()
return done(null, user);
} catch (error) {
return done(error, null);
}
});

How to Check Current Password And (If Correct) Update Password Using Passport.js Local Strategy

I need to write commonplace code to check a Current User Password value submitted via form to see if it matches the existing password in the database, and if so, update the password to a New Password value that was submitted via the same form.
Yet, I can't find any good examples of how to do this using Passport.js. Can anyone advise as to how I can do this in my Users controller down below, if there are any helper functions provided by passport that I should use for this, and how I do this with hashed and salted passwords?
Here is my code:
// Form Submitted
req.body = {
_id: '5294198b7b35ad2794000001',
email: 'testusera1#abc.net',
name: 'John Smith',
provider: 'local',
username: 'ab123',
current_password: 'currentpassword',
new_password: 'newpassword'
}
// Route
app.put('/users/me', users.update);
// Controller
var mongoose = require('mongoose'),
User = mongoose.model('User'),
_ = require('underscore'),
passport = require('passport'),
LocalStrategy = require('passport-local').Strategy;
exports.update = function(req, res) {
var user = req.user
user = _.extend(user, req.body);
user.save(function(err) {
if(err) { console.log(err) };
res.jsonp(user);
});
};
// Passport Config File
module.exports = function(passport) {
//Serialize sessions
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findOne({
_id: id
}, function(err, user) {
done(err, user);
});
});
//Use local strategy
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
},
function(email, password, done) {
User.findOne({
email: email
}, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {
message: 'Unknown user'
});
}
if (!user.authenticate(password)) {
return done(null, false, {
message: 'Invalid password'
});
}
return done(null, user);
});
}
));
};
hashed and salted password
full example on github
// Bcrypt middleware
userSchema.pre('save', function(next) {
var user = this;
if(!user.isModified('password')) return next();
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if(err) return next(err);
bcrypt.hash(user.password, salt, function(err, hash) {
if(err) return next(err);
user.password = hash;
next();
});
});
});
// Password verification
userSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if(err) return cb(err);
cb(null, isMatch);
});
};

Categories

Resources