bcrypt error with passport and mongoose - javascript

Bcrypt is throwing an Incorrect arguments error which I traced back to this function in user.js
userSchema.methods.comparePassword = (candidatePassword, callback) => {
bcrypt.compare(candidatePassword, this, (err, isMatch) => {
console.log('candidatePassword= ', candidatePassword, '& this= ', this);
if (err) { return callback(err); }
callback(null, isMatch);
});
};
/*
candidatePassword= bird
this= {}
this.password= undefined */
The user object is coming back as an empty object, and therefore this.password is undefined. I assume the this parameter in bcrypt.compare refers to the userSchema instance. The userSchema is declared in passport.js
const passport = require('passport');
const ExtractJwt = require('passport-jwt').ExtractJwt;
const JwtStrategy = require('passport-jwt').Strategy;
const LocalStrategy = require('passport-local').Strategy;
const User = require('../models/user');
const config = require('../config');
var localOptions = {
usernameField: 'email',
};
// Verifies user by checking if a password matches the specified email during signin
var localStrategy = new LocalStrategy(localOptions, function (email, password, done) {
User.findOne({ email:email.toLowerCase()}, function (err, user) {
console.log('/passport.js/localStrategy- user object: ', user)
if (err) { return done(err); }
if (!user) { return done(null, false); }
user.comparePassword(password, function (err, isMatch) {
console.log('/passport.js/localStrategy- password: ', password)
if (err) { return done(err); }
if (!isMatch) { return done(err, false); }
return done(null, user);
});
});
});
// ... jwt strategy ...
passport.use(localStrategy);
/*
user object: { _id: 58a1018dc3f89eb5955b8638,
email: 'bird#bird.com',
password: '$2a$10$lAJ9hoGKt9ggfk1TISfkOedxDIs/waLB5e4PccHAKt286XCKCY0/q',
__v: 0 } */
I'm not sure quite what the issue as it seems a user object is returned with an encrypted password field from mongodb, and user.comparepassword() is called...
I signed the user up with the same Schema object as well.
Any help / tips appreciated!

You are only setting up your model so that it pulls in the candidatePassword but never finds the stored password from the database. Since this is returning an empty object, either the email is not being matched or the password is not being compared to what is stored. Try simplifying the comparePassword function and adding 'sync' to the bcrypt.compare which removes the need for a callback.
In models:
userSchema.methods.comparePassword = (candidatePassword) => {
return bcrypt.compareSync(candidatePassword, this.password);
};

Related

user.findOne() always returns null

I am trying to get mongoose return data from local MongoDB instance. Eveything else is running fine. The form is giving data to passport. I have also inserted data into mongodb manually. The only error is in user.findOne. When I print user into console, instead of returning some data it returns null
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const User = require('../models/member');
// authentication using passport
passport.use(new LocalStrategy({
usernameField: 'user',
passwordField: 'password'
},
function (username, password, done) {
User.findOne({ user: username }, function (err, user) {
// console.log(user, username, password);
if (err) {
console.log(`Error in configuring passport-local \n ${err}`);
return done(err);
}
if (!user) {
console.log(`Invalid username or password!!`)
return done(null, false);
}
return done(null, user);
});
}
));
My schema:
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
user: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
dept: String,
yr: Number,
name: {
type: String
}
}, {
timestamps: true
});
module.exports = mongoose.model('member_list', userSchema, 'member_list');
Here's my full code: https://github.com/krush11/site
Let me know if you need any more info
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const User = require('../models/member');
// authentication using passport
passport.use(new LocalStrategy({
usernameField: 'user',
passwordField: 'password'
},
async function (username, password, done) {
await User.findOne({ user: username }, function (err, user) {
// console.log(user, username, password);
if (err) {
console.log(`Error in configuring passport-local \n ${err}`);
return done(err);
}
if (!user) {
console.log(`Invalid username or password!!`)
return done(null, false);
}
return done(null, user);
});
}
));
The function looks good but try making it asynchronous then await the query from the database. Take a look at the modification I made to your code. You might also want to refactor your code and use promises instead of callback and wrap everything inside of try{} catch{} block.
But making the function asynchronous and awaiting the query will fix the issue, this is because the findOne() method actually returns a query and might not have completed before you console it.
Let me know if you still have any issue.

req.user is undefined when using Passport-jwt on fetch user

I already checked multiple answers on Stackoverflow, and also went through on the documentation but I still cannot figure out my problem. when I try to sign in and signup it's working perfectly I have my token. It's nightmare to just fetch my current_user get('/isAuth') I get undefined !!!!
const Authentication = require("../controllers/authentication");
const passport = require("passport");
const requireAuth = passport.authenticate('jwt', {session: false});
const requireSignin = passport.authenticate('local', {session: false});
module.exports = app => {
app.post('/signup', Authentication.signup);
app.post('/signin', requireSignin, Authentication.signin);
// Current User is undefined !!!!!
app.get('/isAuth', Authentication.fetchUser);
my passport.js
const keys = require("../config/keys");
const passport = require("passport");
const User = require("../models/User");
const JwtStrategy = require("passport-jwt").Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const localStrategy = require("passport-local");
// Create local strategy
const localOptions = { usernameField: "email" };
const localLogin = new localStrategy(localOptions, function(email,password,done) {
// verify this username and password, call done with the user
// if it is the correct username and password
// otherwise, call done with false
User.findOne({ email: email }, function(err, user) {
if (err) {return done(err);}
if (!user) {return done(null, false);}
// compare passwords - is password is equal to user.password?
user.comparePassword(password, function(err, isMatch) {
if (err) {return done(err);}
if (!isMatch) {return done(null, false);}
return done(null, user);
});
});
});
// setup option for jwt Strategy
const jwtOptions = {
jwtFromRequest: ExtractJwt.fromHeader('authorization'),
secretOrKey: keys.secret
};
// Create Jwt strategy
const jwtLogin = new JwtStrategy(jwtOptions, function(payload, done) {
// See if the user Id in the payload exists in our database
// If does, call 'done' with that other
// otherwise, call done without a user object
User.findById(payload.sub, function(err, user) {
if (err) {return done(err, false);}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
});
// Tell passport to use this strategy
passport.use(jwtLogin);
passport.use(localLogin);
// Generate token
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id).then(user => {
done(null, user);
});
});
./controller/authentication.js
const User = require('../models/User');
const jwt = require('jwt-simple');
const config = require('../config/keys');
function tokenForUser(user){
const timestamp = new Date().getTime();
return jwt.encode({sub: user.id, iat: timestamp}, config.secret);
}
exports.signup = function(req,res,next){
console.log(req.body)
const email = req.body.email;
const password = req.body.password;
if(!email || !password){
return res.status(422).send({error: 'You must provide email and password'});
}
// See if user with the given email exists
User.findOne({email: email}, function(error, existingUser){
if (error){return next(error)};
// if a user with email does exist, return an error
if (existingUser){
return res.status(422).send({error: 'Email is in use'});
}
// if a user with email does not exist, create and save record
const user = new User({
email: email,
password: password
});
user.save(function(error){
if (error){return next(error);}
// respond to request indicating the user was created
res.json({token: tokenForUser(user)});
})
})
}
exports.signin = function (req,res,next){
// user has already had their email and password auth
// we just need to give them a token
res.send({token: tokenForUser(req.user)});
}
// here is my problem...
exports.fetchUser = function (req, res, next) {
console.log('this is ',req.user)
};
Still stuck for many days... it's a nightmare!!! if someone has the solution.
after sign in if I want to go to my route /isAuth to check my user data:
Have your tried using a middleware that calls the isAuthenticated function on req object? This function is added by passport and is generally the recommended way to check if a request is authenticated.
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect("/");
}
Then you can call this function when a user hits your isAuth route:
app.get('/isAuth', isLoggedIn, Authentication.fetchUser);

"this" is not refering the the user object in mongoose model

I have the following code to save user object to database from express,
api.post('/signup', function (req, res) {
var user = new User();
user.name = req.body.name;
user.email = req.body.email;
user.setPassword(req.body.password);
user.save(function (err) {
err ? res.send(err) : res.json({ message: 'User Created!'})
})
})
and below here for the user schema,
var mongoose = require('mongoose');
var bcrypt = require('bcrypt');
var SALT_WORK_FACTOR = 10;
var Schema = mongoose.Schema;
var userSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
unique: true,
required: true
},
password: String,
})
userSchema.methods.setPassword = function (password) {
bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
if (err) return console.log(err);
bcrypt.hash(password, salt, function (err, hash) {
if (err) return console.log(err);
this.password = hash; // <= `this` will not be saved to mongoDB
})
})
}
module.exports = mongoose.model('User', userSchema);
When it performed the save function, it will show that the password was undefined and will save the object to mongodb without password value.
I've checked the this question as well and changed all my functions to not using arrow method but it still got the same error.
The same issue when i was using middleware hook that this reference was not referring to user object. Below here for my another approach,
userSchema.pre('save', (next) => {
bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
if(err) return next(err);
bcrypt.hash(this.password, salt, function (err, hash) {
if (err) return next(err);
this.password = hash; // <= `this` will not be saved to mongoDB
next();
})
})
})
Any idea for this value to be saved to database when perform save?
You should try below code, Hope it will work for you:
userSchema.methods.setPassword = function (password) {
var pswd=password;
bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
if (err) return console.log(err);
bcrypt.hash(pswd, salt, function (err, hash) {
if (err) return console.log(err);
pswd = hash;
console.log(pwsd); // your decripted password
})
})
}
I will elaborate more in the next days (I want to test a couple of things) but the error, I think, is because the meaning of this in JS is tricky.
Basically this is not a reference to the function's lexical scope, it is binding done in the context where the function is called.
So, as a fast solution while I elaborate more, I would recommend you to encrypt the password in:
user.setPassword(encrypt(req.body.password)); and simplify the mongoose's setPassword function
Hope that helps.
If you want this to refer to the userSchema context, you can use arrow functions for the bcrypt callbacks. Arrow functions will not create a new function scope, so it will preserve the original context.
userSchema.methods.setPassword = function (password) {
bcrypt.genSalt(SALT_WORK_FACTOR, (err, salt) => {
if (err) return console.log(err);
bcrypt.hash(password, salt, (err, hash) => {
if (err) return console.log(err);
this.password = hash; // <= `this` will be the user object
})
})
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Compare passwords BcryptJS

So I'm trying to build a very basic user login. I'm trying to create a user, then login with those credentials and get back a JSON Web Token. Where I'm stuck is trying to compare the passwords then send a response.
Steps:
Create User:
enter email and password
salt/hash user password
store user into database
return success
Login
find user by request email value
if found compare passwords
passwords good send JSON Web Token
User Model
email:{
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
}
User Routes
var express = require('express');
var router = express.Router();
var jwt = require('jsonwebtoken');
var bcrypt = require('bcryptjs');
// Create User
...
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash("superSecret", salt, function(err, hash) {
user.password = hash;
user.save();
res.json({success: true, message: 'Create user successful'});
});
});
...
// Login
...
bcrypt.compare(req.body.password, 'superSecret', function(err, res) {
if(req.body.password != user.password){
res.json({success: false, message: 'passwords do not match'});
} else {
// Send JWT
}
});
So the two problems here is that, I can't send a response nor can I compare the password. Just completely stuck on this, any help would be greatly appreciated.
As described in the doc, you should use bcrypt.compare like that:
bcrypt.compare(req.body.password, user.password, function(err, res) {
if (err){
// handle error
}
if (res) {
// Send JWT
} else {
// response is OutgoingMessage object that server response http request
return response.json({success: false, message: 'passwords do not match'});
}
});
And here is a nice post about Password Authentication with Mongoose (Part 1): bcrypt
//required files
const express = require('express')
const router = express.Router();
//bcryptjs
const bcrypt = require('bcryptjs')
//User modal of mongoDB
const User = require('../../models/User')
//Post request for login
router.post('/', (req, res) => {
//email and password
const email = req.body.email
const password = req.body.password
//find user exist or not
User.findOne({ email })
.then(user => {
//if user not exist than return status 400
if (!user) return res.status(400).json({ msg: "User not exist" })
//if user exist than compare password
//password comes from the user
//user.password comes from the database
bcrypt.compare(password, user.password, (err, data) => {
//if error than throw error
if (err) throw err
//if both match than you can do anything
if (data) {
return res.status(200).json({ msg: "Login success" })
} else {
return res.status(401).json({ msg: "Invalid credencial" })
}
})
})
})
module.exports = router
If we you to use bcryptjs in browser(HTML) then you can add bcryptjs CDN to do this.
CDN - https://cdn.jsdelivr.net/npm/bcryptjs#2.4.3/dist/bcrypt.js
Example-
HTML- (Add above CDN in tag)
JS-
var bcrypt = dcodeIO.bcrypt;
/** One way, can't decrypt but can compare */
var salt = bcrypt.genSaltSync(10);
/** Encrypt password */
bcrypt.hash('anypassword', salt, (err, res) => {
console.log('hash', res)
hash = res
compare(hash)
});
/** Compare stored password with new encrypted password */
function compare(encrypted) {
bcrypt.compare('aboveusedpassword', encrypted, (err, res) => {
// res == true or res == false
console.log('Compared result', res, hash)
})
}
If you want to do same in Nodejs
/** Import lib like below and use same functions as written above */
var bcrypt = require('bcryptjs')
From what I can see your logic is correct.
If you are using mongoose I suggest you to use the pre 'save' hook.
User Schema
userSchema.pre('save', function(next) {
// only hash the password if it has been modified (or is new)
if (!this.isModified('password')) {
return next();
}
// generate a salt
return bcrypt.genSalt(10, function(error, salt) {
if (error) {
return next(error);
}
// hash the password using the new salt
return bcrypt.hash(this.password, salt, function(error, hash) {
if (error) {
return next(error);
}
// override the cleartext password with the hashed one
this.password = hash;
return next();
});
});
});
userSchema.methods.comparePassword = function(passw, cb) {
bcrypt.compare(passw, this.password, function(err, isMatch) {
if (err) {
return cb(err, false);
}
return cb(null, isMatch);
});
};
And in your routes:
Login
...
return user.comparePassword(password, function(error, isMatch) {
var payload = {
iat: Math.round(Date.now() / 1000),
exp: Math.round((Date.now() / 1000) + 30 * 24 * 60),
iss: 'Whatever the issuer is example: localhost:3000',
email: user.email
};
var token = jwt.encode(payload, 'secret');
if (isMatch && !error) {
// if user is found and password is right create a token
return res.json({
success: true,
token: `JWT ${token}`,
user: user,
msg: 'Authentication was succesful'
});
}
return next({code: 401, msg: 'Password is incorrect'});
});
});
Create user
// Pre hook will take care of password creation
return user.save()
.then(function(user) {
var payload = {
iat: Math.round(Date.now() / 1000),
exp: Math.round((Date.now() / 1000) + 30 * 24 * 60),
iss: 'Whatever the issuer is example: localhost:3000',
email: user.email
};
var token = jwt.encode(payload, 'secret');
return res.status(201).json({user, token: `JWT ${token}`, msg: 'User was succesfully created'});
})
.catch((err) => next(err));
bcrypt.compare(req.body.password, user.password, function(err, results){
if(err){
throw new Error(err)
}
if (results) {
return res.status(200).json({ msg: "Login success" })
} else {
return res.status(401).json({ msg: "Invalid credencial" })
}
})
const bcrypt = require("bcryptjs");
const salt = bcrypt.genSaltSync(10);
const hashPassword = (password) => bcrypt.hashSync(password, salt);
const comparePassword = (password, hashedPassword) =>
bcrypt.compareSync(password, hashedPassword);
bcrypt.compare(req.body.password, user.password)
.then(valid => {
if (!valid) {
return res.status(401).json({ message: 'Paire login/mot de passe incorrecte' });
}
res.status(200).json({
userId: user._id,
token:jwt.sign(
{userId: user._id},
process.env.ACCESS_TOKEN_SECRET_KEY,
{expiresIn:'24h'}
),
message: 'connected'
});
})
.catch(error => res.status(500).json({ error }));
enter code here

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);
}
});

Categories

Resources