Related
I have a node.js login system with passport but I am trying to figure out how to log in a user with either their username or email. I am only able to log in the user with email or username seperately. I don't know how to write the code to cater for both their username and email. So if a user wants to login with username, they can or if the wish to use their email, they also can. Here is my localstrategy code in my users.js file:
passport.use(new LocalStrategy(
function(email, password, done) {
User.getUserByEmail(email, function(err, user, next){
if(err) throw err;
if(!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 {
return done(null, false, {message: 'Invalid password'});
}
});
});
}));
And here's my module.exports in my user.js:
module.exports.getUserByEmail = function(email, callback){
var query = {email: email};
User.findOne(query, callback);
}
module.exports.getUserById = function(id, callback){
User.findById(id, callback);
}
module.exports.comparePassword = function(candidatePassword, hash, callback){
bcrypt.compare(candidatePassword, hash, function(err, isMatch) {
if(err) throw err;
callback(null, isMatch);
});
}
The above code only allows user to login with their email. I want users to have the opportunity to login with either their email or username.
Just got into this situation, and I though I'll share my final code to verify user using either username or email:
userSchema.statics.findByCredentials = async credentials => {
const {
email = undefined,
username = undefined,
password = undefined
} = credentials
if ((!!email && !!username) || (!email && !username)) {
throw new Error('Should provide either email or username.')
}
if (!password) {
throw new Error('Password is required.')
}
const user = await User.findOne(email ? { email } : { username })
if (!user) {
throw new Error('Credentials are invalid!')
}
if (!bcrypt.compare(password, user.password)) {
throw new Error('Credentials are invalid!')
}
return user
}
So I am using this function to verify if the user provided valid credentials, and from my handler, I call the function on the model class.
Expect both username and password within your authentication middleware and then proceed with whatever value you have found as the condition to find the user.
Middleware example:
function authenticateUser(req, res, done) {
let username = req.body.username,
password = req.body.password,
email = req.body.email;
let conditions = !!username ? {username: username} : {email: email};
UserModel.findOne(conditions, (err, user) => {
if (err) return done(err);
if (!user) return done(new Error('Incorrect username or email'));
return user.comparePassword(password, user.password)
.then(match => {
if (match) return done();
else return done(new Error('Incorrect password'));
})
.catch(error => {
if (error) return done(new Error(`Unable to validated password. - ${error}`));
});
});
}
Now, a front-end developer — with the right documentation — can now actually use either the username, email or both (you will need a bit of JavaScript for both) when building login forms using your endpoint. Here is an example of using both:
HTML:
<form id="login-form" method="POST" action="/login">
<input id="username-or-email" type="text" placeholder="Username or Email" required/>
<input type="password" name="password" placeholder="Password" required/>
<input type="submit"/>
</form>
JavaScript:
// select the form element
let loginForm = document.querySelector('#login-form');
// add a form handler on submit
loginForm.addEventListener("submit", formHandler);
// validate and the set name attribute as appropriate
function formHandler() {
/** W3C Email regex: (RFC5322) */
const email_regex = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
/** Must starts with a letter then can include underscores (_) & hyphens (-) */
const username_regex = /^[a-zA-Z][\w-]+$/;
let input = document.querySelector('#username-or-email');
if (email_regex.test(input.value)) {
// it is an email, send the value as an email
input.setAttribute("name", "email");
} else if (username_regex.test(input.value)) {
// it is a username, send the value as a username
input.setAttribute("name", "username");
} else {
// invalid email or username format, return an error message to user
}
}
So you get to validate and set your dynamic input at the same time. Keep in mind that the regular expression should match your username and email data model as much as possible.
You can use getUserById inside the callback of getUserByEmail so that both of the queries run for either email or username. If you specify email, then emailOrUserName will have the email value and then User.getUserByEmail will return the user and next it will proceed with comparePassword.
And if you have username then emailOrUserName will have the username value and then User.getUserByEmail will not return the user so, it executes User.getUserById and if the user is found there then it will proceed with User.comparePassword else it will return Unknown user
passport.use(new LocalStrategy(
function(emailOrUserName, password, done) {
User.getUserByEmail(emailOrUserName, function(err, user, next){
if(err) throw err;
if(!user){
User.getUserById(emailOrUserName, function(err, user, next){
if(err) throw err;
if(!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 {
return done(null, false, {message: 'Invalid password'});
}
});
});
}));
Do this in local strategy
function(username, password, done) {
var criteria = (username.indexOf('#') === -1) ? {username: username} : {email: username};
User.findOne(criteria, function (err, user) { //implementation }
The code below can be used to allow your users to log in using either their username or email and password:
if (!req.body.username && !req.body.email) {
res.status(400).send({ message: "All input is required, username/email is missing",
status: false });
return;
}
if (!req.body.password) {
res.status(400).send({ message: "All input is required, password is missing",
status: false });
return;
}
let whereClause = {};
if (req.body.email) {
whereClause.email = req.body.email;
} else if (req.body.username) {
whereClause.username = req.body.username.toLowerCase();
}
const user = await User.findOne({ where: whereClause });
// If user not found
if (!user) {
return res.status(404).send({ message: "User not found", status: false });
}
You can wrap it in a try-catch block for proper error handling.
I add little more on the above answer shared by user7153178: Mine is related to the backend, using context.
Inside your Login component in the frontend, try to have these.
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
then you will be needing this function on your form:
const handleSubmit = () => {
const user = {
email,
password,
};
if (email === "" || password === "") {
setError("Please fill in your credentials");
} else {
loginUser(user, context.dispatch);
}
};
this is one of the input as part of your form
<Input
placeholder={"Email or Phone Number"}
name={"email"}
id={"email"}
value={email}
onChangeText={(text) =>
setEmail(text.toLowerCase())}
/>
In the backend write your post like that:
router.post("/login", async (req, res) => {
const user = await User.findOne(
{ $or: [{ email: req.body.email }, { phone:
req.body.email }] }
);
if (!user) {
return res.status(400).send("The user not found");
}
if (user && bcrypt.compareSync(req.body.password,
user.passwordHash)) {
const token = jwt.sign(
{
userId: user.id,
isAdmin: user.isAdmin,
},
secret,
{ expiresIn: "1d" }
);
res.status(200).send({ user: user.email, token:
token });
} else {
res.status(400).send("password is wrong!");
}
});
I've seen this question asked already, sorry if it seems repetitive; but I've read through a lot of the answers and they haven't helped. I keep getting a "MISSING CREDENTIALS" error no matter what I do with the login form. I'm trying to implement a user login using email and password.
My Route File
const express = require('express');
const router = express.Router();
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const User = require('../models/user');
passport.use(new LocalStrategy((email, password, done) => {
User.getUserByEmail(email, (err, user) => {
if(err) throw err;
if(!user) {
return done(null, false, {message: 'this account does not exist'});
}
User.comparePassword(password, user.password, (err, isMatch) => {
if(err) throw err;
if(isMatch) {
return done(null, user);
}
else {
return done(null, false, {message: 'oops! wrong password! try again'});
}
});
});
}));
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.getUserById(id, (err, user) => {
done(err, user);
});
});
router.post('/', (req, res, next) => {
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/toSignInPage',
failureFlash: true
})(req, res, next);
});
module.exports = router;
user.js file
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const bcrypt = require('bcryptjs');
const UserSchema = new Schema({
email: {
type: String,
required: true,
trim: true
},
name: {
type: String,
required: true,
unique: true,
trim: true
},
password: {
type: String,
required: true,
trim: true
},
profilePhoto: {
originalemail: String,
imagePath: String
},
token: String
});
const User = module.exports = mongoose.model('User', UserSchema);
module.exports.registerUser = function(newUser, callback) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if(err) {
console.log(err);
}
newUser.password = hash;
newUser.save(callback);
});
});
};
module.exports.getUserByEmail = function(email, callback) {
const query = {
email: email
};
User.findOne(query, callback);
};
module.exports.getUserById = function(id, callback) {
User.findById(id, callback);
};
module.exports.comparePassword = function(candidatePassword, hash, callback) {
bcrypt.compare( candidatePassword, hash, (err, isMatch) => {
if(err) throw err;
callback(null, isMatch);
});
};
I've initialized passport in my app.js file - session options and all that nice stuff.
also my html(handlebars)for the form
<form method="post" action="/signInPage">
<label for="mail"> Email </label>
<input type="email" id="mail" name="email" />
<label for="password"> Password</label>
<input type="password" id="password" name="password"/>
<button type="submit">Sign in</button>
<p class="corp"> Forgot password? </p>
</form>
Any help is appreciated on this. I've been stuck for a while debugging and I can't seem to figure out what I must have done wrong. please if I didn't properly format the question in an understandable format let me know.
Someone who I'm grateful to posted a link to the solution; but I can't find the comment, so I'll just add the solution.
Passport js logs user in with user name by default
passport.use(new LocalStrategy((username, password, done) => {
}));
so I was trying to use email
passport.use(new LocalStrategy((email, password, done) => {
}));
in order to log a user in with email, I had to do it this way
passport.use(new LocalStrategy((email, password, done) => {
{
usernameField : 'email',
passwordField : 'password'
}
}));
this will override the default use of username to email...
here's the link to the soultion:
https://github.com/scotch-io/easy-node-authentication/blob/master/config/passport.js#L58
I'm writing a login in Node.js. It used to work just fine, but now I'm trying to add a field and it stopped working and I have no idea why. I'm using Passport.js to do the authentication.
The weird part is that it does add the user to the database, but it switches up the fields. Name is password etc.
The error I'm getting is as follows;
C:\Users***\Documents\GitHub\Query\config\passport.js:33
return done(null, false, req.flash('signupMessage', 'Je email bestaat al.'));
^
TypeError: done is not a function
at Query. (C:\Users***\Documents\GitHub\Query\config\passport.js:33:28)
passport.js
var LocalStrategy = require('passport-local').Strategy;
var FacebookStrategy = require('passport-facebook').Strategy;
var User = require('../models/login.js');
var configAuth = require('./auth');
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({
nameField: 'name',
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
function (req, name, email, password, done) {
User.findOne({
'local.email': email
}, function (err, user) {
if (err)
return done(err);
if (user) {
return done(null, false, req.flash('signupMessage', 'Je email bestaat al.'));
} else {
var newUser = new User();
newUser.local.name = name;
newUser.local.email = 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, name, email, password, done) {
User.findOne({
'local.email': email
}, function (err, user) {
if (err)
return done(err);
if (!user)
return done(null, false, req.flash('loginMessage', 'De gebruiker bestaat niet. Maak een account!'));
if (!user.validPassword(password))
return done(null, false, req.flash('loginMessage', 'Wachtwoord is verkeerd. Probeer het opnieuw!'));
return done(null, user);
});
}));
login model
var mongoose = require('mongoose'); var bcrypt = require('bcrypt-nodejs');
var userSchema = mongoose.Schema({
local : {
name : String,
email : String,
password : String,
},
facebook : {
id : String,
token : String,
email : String,
name : String
} });
userSchema.methods.generateHash = function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null); };
userSchema.methods.validPassword = function(password) {
return bcrypt.compareSync(password, this.local.password); };
module.exports = mongoose.model('User', userSchema);
Routes
module.exports = function (app, passport) {
app.get('/', function (req, res) {
res.render('login.ejs', {
message: req.flash('loginMessage')
});
});
app.get('/register', function (req, res) {
res.render('index.ejs', {
message: req.flash('signupMessage')
});
});
app.post('/', passport.authenticate('local-login', {
successRedirect: '/topic',
failureRedirect: '/',
failureFlash: true
}));
app.post('/register', passport.authenticate('local-signup', {
successRedirect: '/topic',
failureRedirect: '/register',
failureFlash: true
}));
};
Form part of the view
<form action="/register" method="post">
<div class="form-group">
<input type="text" placeholder="Name" class="form-control nameForm" name="name">
</div>
<!-- END FORM GROUP -->
<div class="form-group">
<input type="text" placeholder="Email" class="form-control emailForm" name="email">
</div>
<!-- END FORM GROUP -->
<div class="form-group">
<input type="password" placeholder="Password" class="form-control" name="password">
</div>
<!-- END FORM GRROUP -->
<button type="submit" class="btn btn-primary btn-default">Registeer</button>
</form>
The verification callback signature for LocalStrategy is incorrect:
passport.use('local-signup', new LocalStrategy({
nameField: 'name',
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
}, function (req, name, email, password, done) { ... })
The correct signature:
function(req, email, password, done) { ... }
(no name argument)
That also explains why the fields seem to be shifted.
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);
}
});
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);
});
};