User login with Passport js and mongoDB - javascript

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

Related

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

Logging with role in Node.js and Express

I'm new in Node.js and Express and I have a problem with access control with Passport.
I want to check with which role user is logged in.
My user schema:
let mongoose = require('mongoose');
let userSchema = new mongoose.Schema({
name:{
type: String,
required: true
},
email:{
type: String,
required: true
},
password:{
type: String,
required: true
},
username:{
type: String,
required: true
},
role:{
type: String,
default: 'user'
},
activated:{
type: Boolean,
default: false
}
});
let User = module.exports = mongoose.model('User', userSchema);
My passport.js file
const LocalStrategy = require('passport-local').Strategy;
const User = require('../models/user');
const dbConfig = require('../config/database');
const bcrypt = require('bcrypt');
module.exports = (passport) => {
//Local Strategy
passport.use(new LocalStrategy((email, password, done) => {
//Match email
let query = {email: email}
User.findOne(query, (err, user) => {
if(err){
console.log(err);
}
if(!user) {
return done(null, false, {message: 'No user found'});
}
//Match password
bcrypt.compare(password, user.password, (err, isMatch) => {
if(err){
console.log(err);
}
if(isMatch){
return done(null, user);
} else {
return done(null, false, {message: 'Wrong password'});
}
});
});
}));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
}
The small part of app.js file where I'm setting up global variable user
app.get('*', (req, res, next) => {
res.locals.user = req.user || null;
next();
});
Part of layout.pug file where I'm using global user variable
.navbar.collapse.navbar-collapse
ul.nav.navbar-nav
li
a(href='/') Home
if user
li
a(href='/articles/add') Add article
What I want to do is to check if user has an admin role. I tried to do that in different ways but with no success. I wanted to check the role in app.js, but I was getting errors:
if(req.user.role == 'admin'){
res.locals.admin = req.user;
}
Also I wanted to pass only the role in passport.js and it was working then but I need also other property of user.
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
How can I resolve my problem?
You should check if req.isAuthenticated() then checking the role. Cuz if req.user is null then you can't check the property 'admin' of null
if(req.isAuthenticated() && req.user.role == 'admin'){
res.locals.admin = req.user;
}
The req.isAuthenticated() function is provided by passport.js

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?

Node.js - Is not a function

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.

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