Reputation: 2157
I have an authentication system setup in my express node application, but I’ve been experiencing some errors with the passport authentication that I setup. For example, the registration system works fine, as does my validation system (which basically checks whether the user/pass combo is valid for my school’s grade book login). However, the actual passport login (which checks if the user/pass combo is a registered user in my mongoose database) is not functioning correctly.
Relavent part of app.js
//passport setup
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy(function (username, password, done) {
User.findOne({username: username}, function (err, user) {
user.checkLogin(password, function (error, userIsValid) {
if (error) {
console.log('Error:');
return done(error);
}
if (userIsValid) {
console.log('Valid:');
}
});
});
}));
user.js
var mongoose = require('mongoose');
var passportLocalMongoose = require('passport-local-mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt');
var User = new Schema({
username: String,
password: String,
studentID: {type: Number}
});
User.methods.checkLogin = function (password, callback) {
bcrypt.compare(password, this.password, function (error, same) {
if (error){
callback(error);
}
callback(same);
})
};
User.plugin(passportLocalMongoose);
module.exports = mongoose.model('User', User);
and finally, index.js
var express = require('express');
var router = express.Router();
var passport = require('passport');
var User = require('../models/user');
var request = require('request');
var cheerio = require('cheerio');
//register
router.post('/register', function(req, res){
var username = req.body.username;
var password = req.body.password;
var studentID = req.body.studentID;
areCredentialsValid(username, password, function (statusBoolean) {
if (statusBoolean === true){
User.register(new User({
username: username,
password: password,
studentID: studentID
}), password, function (){
console.log('Registered:');
res.redirect('./');
res.end()
})
}else{
console.log('Invalid Credentials:');
res.redirect('./');
res.end()
}
});
});
function areCredentialsValid(username, password, callback){
if (typeof username !== 'undefined' && username !== null && username !== '' &&
typeof password !== 'undefined' && password !== null && password !== ''){
var cookie = {};
var responseBoolean = false;
var config = {
method: 'GET',
url: 'https://parents.mtsd.k12.nj.us/genesis/j_security_check',
headers: {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36'
}
};
request(config, function (error, response, body) {
cookie = response.headers['set-cookie'];
console.log(cookie);
var config = {
method: 'POST',
url: 'https://parents.mtsd.k12.nj.us/genesis/j_security_check',
form: {
'j_username': username,
'j_password': password
},
headers: {
'Cookie': cookie,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36'
}
};
request(config, function (error, response2, body) {
//console.log(response);
console.log(response2.headers);
if (response2.headers['location'] === '/genesis/parents?gohome=true'){
responseBoolean = true;
}else{
responseBoolean = false;
}
callback(responseBoolean);
return responseBoolean;
})
})
}
}
//login
router.post('/login', function(req, res) {
passport.authenticate('local', function (error, user, info){
if (user === false) {
// handle login error ...
console.log('failure');
} else {
// handle successful login ...
console.log('success');
}
});
});
module.exports = router;
All help is appreciated. If its easier to deal with, the source code can be found here.
UPDATE: So with the latest suggestions, I get the following output: And thus the login is functioning. However, the thing is, the login seems to be looping and recognizing the user once, then showing the "invalid login" message rather than just finishing the process
Upvotes: 1
Views: 2454
Reputation: 1134
I found 2 issues.
First:
In the app.js file, after you configure the LocalStrategy, you have to call done after you check the user credentials.
See:
passport.use(new LocalStrategy(function (username, password, done) {
User.findOne({username: username}, function (err, user) {
user.checkLogin(password, function (error, userIsValid) {
// You function should return the user data, not just if it is valid or not.
if (error) {
console.log('Error:');
return done(error);
}
if (userIsValid) {
console.log('Valid:');
// call done here
return done(null, {_id: 123 /* data to represent the user*/});
} else {
// call done here when the user is not valid
console.log('Not Valid:');
return done(null, null, 'Invalid credentials (or something like this.)');
}
});
});
}));
Two:
In your login route configuration, you have treat the error calling the next function, and you error handler function will catch it.
//login
router.post('/login', function(req, res, next) {
passport.authenticate('local', function (error, user, info){
if (user === false) {
// handle login error ...
next(new Error('AuthenticationError'), req, res);
} else {
// handle successful login ...
res.redirect('/')
}
})(req, res, next);
});
EDIT for the last comment:
You have to check not just if the user isn't false
, but also, it there is no error, look:
//login
router.post('/login', function(req, res, next) {
passport.authenticate('local', function (error, user, info){
// A error also means, an unsuccessful login attempt
if(error) {
console.error(error);
console.log('Failed login:');
// And do whatever you want here.
return next(new Error('AuthenticationError'), req, res);
}
if (user === false) {
// handle login error ...
console.log('Failed login:');
return next(new Error('AuthenticationError'), req, res);
} else {
// handle successful login ...
console.log('Successful login:');
res.redirect('./');
}
})(req, res, next);
});
This worked for me here.
About the (req, res, next)
, it is the arguments to the middleware.
So you call the /login route, you got the request and response, next, you call the passport.authenticate
. This method, returns a function(req, res, next)
, and finally, you pass the request and response for it.
See the code snipped from passport.authenticate
:
module.exports = function authenticate(passport, name, options, callback) {
...
// This is the function you call, when you do: (req, res, next)
return function authenticate(req, res, next) {
...
Check this out for more examples about middlewares.
Hope it's clear now.
Upvotes: 2
Reputation: 1705
Problem is when validation is done you are missing a verify callback
. Please try the below code:
In the below code when you create a LocalStrategy you have three parameter in the function namely username, password and done. Now when your if (userIsValid)
is valid you need to invoke verify callback which is done(null, user)
. So, that you can get the user
when authenticated using passport.authenticate
.
passport.use(new LocalStrategy(function (username, password, done) {
User.findOne({username: username}, function (err, user) {
user.checkLogin(password, function (error, userIsValid) {
if (error) {
console.log('Error:');
return done(error);
}
if (userIsValid) {
console.log('Valid:');
//Below is the code you are missing
return done(null, user)
}
});
});
}));
And the parameter in passport.authenticate
are those which are passed from above verify callback. So, you will need to have only two parameters in callback in this case as below:
passport.authenticate('local', function (error, user){
//your-code
});
Upvotes: 0