DEV Community

Cover image for Building a NodeJS Web App Using PassportJS for Authentication

Building a NodeJS Web App Using PassportJS for Authentication

Richard Debrah on December 20, 2018

Updated!!! This tutorial uses PassportJS to authenticate the NodeJS App with MySQL Database Management Software (DBMS). The reason for this article...
Collapse
 
lawmaina78 profile image
lawmaina78

To get the user model to work, I changed the code into the following

module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('User', {
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
password: {
type: DataTypes.STRING,
allowNull: false
}
}, {
hooks: {
beforeCreate: function(user) {
user.password = bcrypt.hashSync(user.password, bcrypt.genSaltSync(10), null);
}
}
})

// Creating a custom method for our User model.
//This will check if an unhashed password entered by the
//user can be compared to the hashed password stored in our database
User.prototype.validPassword = function(password) {
return bcrypt.compareSync(password, this.password);
};
// Hooks are automatic methods that run during various phases of the User Model lifecycle
// In this case, before a User is created, we will automatically hash their password
/*
User.hook("beforeCreate", function(user) {
user.password = bcrypt.hashSync(user.password, bcrypt.genSaltSync(10), null);
});
*/
return User;
};

Collapse
 
gm456742 profile image
Richard Debrah

I am glad it worked with the update. I will try to figure out your add and incorporate it for others.

Collapse
 
naviaang profile image
Navia

All long day search similar articles and debugging the code, finally, the code can run.

i changed line 27 of index.js
from
const model = sequelize'import';
db[model.name] = model;
become
const model = (path.join(__dirname, file));
sequelize'import';
db[model.name] = model;

But i still wonder why its can work.

And then I try @lawmaina78 , its work too without changing index.js

Note: I newbie, this is my second-day doing nodejs before I'm C# Programmer.

Thanks, Ricard Debrah for the awesome article.

Collapse
 
cristianooo profile image
Cristiano Firmani

Great tutorial I was able to recreate most of it on my own web app but I am getting this error when I try to setup a new account-
Executing (default): SELECT id, FirstName, LastName, Username, Email, Password, createdAt, updatedAt FROM Users AS User WHERE User.email = 'test@test.com' LIMIT 1;
Unhandled rejection Incorrect arguments

Collapse
 
gm456742 profile image
Richard Debrah

@cristiano thank you for taking time to read this article. Can you kindly send all the error output so i can help?
unhandled rejection error from sequelize can be from any number of reasons with respect to the model. make sure you are passing the right associations as it is checked against your model when you make a select/read from the db

Collapse
 
cristianooo profile image
Cristiano Firmani

Sure! I only get that error when setting up a new account but it actually inserts the data into the database so i moved on from it.
Now when I try to login using the credentials I used when I first set up an account, I get a Cannot Get /[object %20Response]

I've switched up the code a little so I'll show you what mine looks like. I have an ejs file with a link to the script and a button that calls the submit function :


<script type="text/javascript" src="/scripts/login.js"></script>
<button type="button" onclick="submit()" class="btn">Sign in</button>

Then the login.js looks like this:


``` function submit() {

var emailInput = document.getElementById("email").value;
var passwordInput =document.getElementById("password").value;

var userData = {
email: emailInput.trim(),
password: passwordInput.trim()
};

if (!userData.email || !userData.password) {
return;
}

// If we have an email and password we run the loginUser function and clear the form
loginUser(userData.email, userData.password);
emailInput.val("");
passwordInput.val("");
};

// loginUser does a post to our "APIlogin" route and if successful, redirects us the the members page
function loginUser(email, password) {
fetch("/APIlogin", {
method: 'POST',
email: email,
password: password
}).then(function(data) {
window.location.replace(data);
// If there's an error, log the error
}).catch(function(err) {
console.log(err);
});
}```

and then finally the app.js and APIlogin call looks like this respectively:


app.post('/APIlogin', passport.authenticate("local"), APIlogin);

and


APIlogin:(req, res)=> {
res.json("/questions");
}

Collapse
 
cristiano profile image
cristiano

I believe this is meant for you, @cristianooo ? Looks interesting though. 😃

Collapse
 
meisterjustice profile image
Justice Eziefule

Thank you soo much man. I encountered a lil problem with User.hooks but it's all good now

change
User.hook("beforeCreate", function(user) {
user.password = bcrypt.hashSync(user.password, bcrypt.genSaltSync(10), null);
});

to

User.beforeCreate(function (user, options) {
user.password = bcrypt.hashSync(user.password, bcrypt.genSaltSync(10), null);
});

Collapse
 
zebatahreen profile image
Zeba

Hey Richard,

Thank you for making an effort to solve the issue, I agree they are various tutorials for MongoDB, EJS, Php, etc.. for the login registration with passport but yours is the only one that I could find with MySQL and HTML. Perfect for what I was looking for.

I need your help for an error,
db[model.name] = model;
^

TypeError: Cannot read property 'name' of undefined

this error is in the index.js file.

Looking forward to your reply.

Collapse
 
paulxevc7 profile image
Paul Valdes

Great Tutorial!
Everything works fine but I have a question. How can I use Auth and return a token using passport?
I'm tying to access with Nebular Auth:
akveo.github.io/nebular/docs/auth/...

Collapse
 
userpaytest profile image
userpay

TypeError: dbUser.validPassword is not a function
at E:\pocnode\src\controllers\admin\auth\passport.js:39:26
at runCallback (timers.js:705:18)
at tryOnImmediate (timers.js:676:5)
at processImmediate (timers.js:658:5)
From previous event:
at E:\pocnode\src\controllers\admin\auth\passport.js:33:53
at process._tickCallback (internal/process/next_tick.js:61:11)

Below i have mention the code

'use strict';
var bcrypt = require('bcrypt-nodejs');
module.exports = (sequelize, DataTypes) => {
var user = sequelize.define('user', {
email: DataTypes.STRING,
password: DataTypes.STRING,
status: DataTypes.INTEGER
}, {});
user.associate = function(models) {
// associations can be defined here
};

user.prototype.validPassword = function (password) {
return bcrypt.compareSync(password, this.password);
};

/* generateHash(password) {
return bcrypt.hash(password, bcrypt.genSaltSync(8));
},
validPassword(password) {
return bcrypt.compare(password, this.password);
}*/
return user;
};

Collapse
 
userpaytest profile image
userpay

I am getting this error in my code please help me to solve this issue.

This model code.
'use strict';
var bcrypt = require('bcrypt-nodejs');
module.exports = (sequelize, DataTypes) => {
var user = sequelize.define('user', {
email: DataTypes.STRING,
password: DataTypes.STRING,
status: DataTypes.INTEGER
}, {});
user.associate = function(models) {
// associations can be defined here
};

user.prototype.validPassword = function (password) {
return bcrypt.compareSync(password, this.password);
};

/* generateHash(password) {
return bcrypt.hash(password, bcrypt.genSaltSync(8));
},
validPassword(password) {
return bcrypt.compare(password, this.password);
}*/
return user;
};

Passport.js code.
var User = require('../../../models/').user;

const LocalStrategy = require('passport-local').Strategy;
var bcrypt = require('bcrypt-nodejs');

module.exports = function (passport) {

passport.serializeUser(function (user, done) {
    done(null, user.id);
});

// used to deserialize the user
passport.deserializeUser(function (id, done) {
     User.findById(id).then(function(user) {
          return done(null, user);
    }).catch(function (err) {
    return done(err),null;
    });

});

passport.use('local-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true // allows us to pass in the req from our route (lets us check if a user is logged in or not)
},
function (req, email, password, done) {
if (email)
// asynchronous
process.nextTick(function () {
User.findAll({where: {email: email,status: 1}}).then( function (user) {
if (!user && user.length==0){
return done(null, false , req.flash('message','All fields are required.'));
}else if (!User.validPassword(password)) {
return done(null, false, req.flash('message','Invalid username or password.'));
}
return done(null, user);
})
.catch(function (error) {
return done(error);
});
});

    }));

};

Collapse
 
modimohini profile image
mohini • Edited

Thank you very much for help. The article is clear to understand.

I am getting this error:
\learningPassportJS\models\index.js:31
db[model.name] = model;
^
TypeError: Cannot read property 'name' of undefined

Not sure what I'm missing :(

Collapse
 
juancodeatatime profile image
Juan Rivera

I got the same error. It took several hours to troubleshoot. The below code worked for me. However, it requires adding "return User" in your js file that contains the User object. In my case, it's in my user.js file.

So, my index.js file code snippet looks like this:

.forEach((file) => {
const model = sequelize.import(path.join(__dirname, file))
console.log("model " + model)
try {
db[model.name] = model
console.log("working")
} catch (err) {
console.error(err)
}

My user.js file contains this important line of code:

return User;
Collapse
 
am8zing profile image
am8zing

Hi there,
anyone getting this?

Cannot GET /[object%20Object]

Collapse
 
am8zing profile image
am8zing • Edited

FIX:
bcrypt-nodejs is no longer supported. npmjs.com/package/bcrypt-nodejs
make sure do get the latest version via npm called bcrypt or bcryptjs.

In user.js just change

var bcrypt = require("bcrypt-nodejs");

to

var bcrypt = require("bcryptjs");

Collapse
 
nijeesh4all profile image
Nijeesh Joshy

Great article man

there is a small typo

..... Handlebars as the ORM and MondoDB as the DBMS which at that time, i was not that .....

You wrote MondoDB instead of mongo db

Collapse
 
gm456742 profile image
Richard Debrah

Thank you Joshy. You are right. I will fix it.

Collapse
 
pravinvram profile image
Pravin

Very detailed , thanks for the same.. am using this to try out nodejs.. will keep this forum updated on how this goes :)

Collapse
 
dvate profile image
Olexiy

Error: Can't set headers after they are sent.
Did someone got same problem?