I'm new to SO & Node / Passport so apologies in advance. I've been trying to work through the login portion of my site using Passport (already have registration working) for several days now with no success. I've tried several tutorials and read through the documentation, but at this point am more confused than when I started.
Problem: passport.authenticate function returns "user" as false despite the information being correct according to my database (MongoDB). I'm not getting any error messages, in /dologin route, the user value is false and info value is "Missing Credentials". Let me know if there is any other information I can provide - I appreciate the help!
Attempts: I've moved my code around to different functions, files etc. Tried different solutions found online, tried the documentation examples, wrote my own methods in my models. etc.
App.js file
app.use(session({
secret: 'secrettexthere',
saveUninitialized: true,
resave: true,
// using store session on MongoDB using express-session + connect
store: new MongoStore({
mongooseConnection: mongoose.connection
})
}));
// Init passport authentication
app.use(passport.initialize());
// persistent login sessions
app.use(passport.session());
passport.use(new LocalStrategy(
function(username, user_password, done) {
User.getUserByUsername(username, function(err,user){
console.log('in getUserByUsername');
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;
console.log('in comparepassword');
if(isMatch) {
console.log('isMatch');
return done(null, user);
} else { console.log('not match');
done(null, false, {message:'Invalid username or password.'})};
})
});
} ));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.getUserById(id, function(err, user) {
done(err, user);
});
});
Routing File (ideally will eventually move this to my controller but just want to get it working to some extent to start)
router.post('/dologin', function(req, res, next) {
console.log(req);
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { console.log('nomatch');
console.log(info);
console.log('');
console.log(user);
return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
console.log('match match match');
return res.redirect('/register');
});
})(req, res, next);
});
Model
module.exports.getUserByUsername = function(username, callback){
var query = {username: username};
User.findOne(query, callback);
};
module.exports.comparePassword = function(candidatePassword, hash, callback){
bcrypt.compare(candidatePassword, hash, function(err,isMatch){
if (err) throw err;
callback(null, isMatch);
});
};
module.exports.getUserById = function(id, callback){
User.findById(id, callback);
};
Related
I've a use-case. My application has multiple users where not all users have the access to all the functionalities in the application. I have to restrict them from accessing the application. For that, I've been writing logic that works as the gateway.
I've created different rules which provides a different kind of authentication methods.
When a user log in to the application, will generate a JWT token upon successful login. That token will be authenticated using the rules given.
Here's my code.
server.js
global.app = new require("express")();
global.passport = require("passport");
require("./bin/kernal");
require('./bin/passport');
----Remaining Code----
TokenAuth.js
var passport = require("passport");
require("../../bin/passport");
module.exports= function (req, res, next) {
passport.authenticate('jwt', function (error, user) {
console.log(user);
if (error) return next(error);
if (!user) return res.status(401).json({"error":"Incorrect data", "status":401, "success":false});
//req.user = user;
next();
})(req, res, next);
};
module.exports.UserAuth = function(req, res, next){ // Error Web Response
passport.authenticate('user_rule', function (error, user) {
if (error) return next(error);
if (!user) return res.status(500).json({"message":"No User found", "status":500, "success":false});
//req.user = user;
next();
})(req, res, next);
};
Passport.js
var passport = require("passport");
app.use(passport.initialize());
app.use(passport.session());
require('../app/middlewares/TokenAuth')
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
User.findOne({_id: id}, function (err, user) {
done(err, user);
});
});
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var options = {};
options.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
options.secretOrKey = process.env.jwt_secret;
passport.use(new JwtStrategy(options, function (payload, done) {
console.log(payload)
User.findOne({_id: payload._id}, function (error, user) {
if (error) return done(error, false);
if (!user) return done(null, false);
// req.auth_user = {"_id":user._id,"email":user.email,"name":user.name,"status":user.status}
done(null, user);
});
}));
passport.use('user_rule', // UserAuth in TokenAuth.js has to use this functionality
new JwtStrategy(options,async function (payload, done) {
console.log(payload)
let err, authUser,companyResource;
[err,authUser] = await to(CompanyContacts.findById(payload._id).populate('company',
["accountid","company_name","email","status"]).lean().exec());
if (err) return done(err, false);
if (!authUser) return done(null, false);
let user= authUser;
if(user.status==true && user.company.status==true){
req.auth_user = {"_id":user._id,"email":user.email,"name":user.fullname,status:user.status,isPrimary:user.primarycontact};
req.auth_company=user.company._id;
req.isAdmin=false;
req.auth_role=user.role;
done(null, user);
}else{
return done(err, false);
}
})
);
user_api.js
router.get("/all/menus",TokenAuth,MenuController.getAllDropDowns);
router.post("/company/user/signin", TokenAuth.UserAuth, AuthController.customerlogin);
Here, when the post api get executed, only the user who gets authenticated has to get the access that api.
But the problem here is, whenever I use this authentication methods, I'm getting the error specified below.
{
"error": "Incorrect data",
"status": 401,
"success": false
}
As per my knowledge, the data is not going into the Passport.js file to get authenticate.
Is this approach good, or do I need to make any changes in this code to make it work?
I'm very new to Passport & Node, I've been trying to solve an issue for several days without being able to find a solution that already exists on SO or the internet. I'm getting no errors or anything on login attempt, nothing in chrome dev, nothing in gitbash. Only problem is page never redirects, never seems to get through the passport.authenticate function inside my auth controller (/dologin). When I attempt a login, the browser never stops loading (ex. the circle keeps spinning for chrome), I have no problems with internet or anything of that nature, I only have this problem when implementing the login feature. One thing that I suspect might be an issue is that I do not have my localstrategy/serialization/deserialization in the right spot but I have tried it in app.js as well so at this point I'm really just too confused.
app.js - I tried including the initialize > session > localstrategy > serialize > deserialize in here, but it also didn't work so I just left initialize and session
// // Init passport authentication
app.use(passport.initialize());
// persistent login sessions
app.use(passport.session());
index.js
// route for login action
router.post('/dologin', auth.doLogin);
authController.js
passport.use(new LocalStrategy(
function (username, password, done) {
User.getUserByUsername(username, function (err, user) {
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' });
}
});
});
}));
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
User.getUserById(id, function (err, user) {
done(err, user);
});
});
userController.doLogin = function(req, res) {
passport.authenticate('local', { successRedirect: '/', failureRedirect: '/doLogin', failureFlash: true }),
function (req, res) {
res.redirect('/');
}
};
users.js (model) - includes getUserByUsername, comparePassword, getUserbyId
module.exports.getUserByUsername = function(username, callback){
var query = {username: username};
User.findOne(query, callback);
}
module.exports.comparePassword = function(candidatePassword, hash, callback){
bcrypt.compare(candidatePassword, hash, function(err, isMatch){
if (err) throw err;
callback(null, isMatch);
});
}
module.exports.getUserById = function(id, callback){
User.findById(id, callback);
}
I build an application using Passport lib using this tutorial (part of it).
Note, I don't need a registration, only login form.
One of the issues is that my LocalStrategy callback is never called. For storing I use mongo:
mongoose.connect(dbConfig.url, {
useMongoClient: true
});
//dbConfig
module.exports = {
'url' : 'mongodb://localhost/passport'
}
Login route looks like this:
module.exports = function(app, passport) {
app.get('/login', function(req, res) {
res.render('login', {
message: req.flash('loginMessage')
});
});
app.post('/login', passport.authenticate('login', {
successRedirect: '/', // redirect to the secure profile section
failureRedirect: '/login', // redirect back to the signup page if there is an error
failureFlash: true // allow flash messages
}));
}
Passport logic is:
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('login', new LocalStrategy({
passReqToCallback: true
}, function(req, username, password, done) {
console.log('start'); // never called
User.findOne({
'local.email': 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', 'Oops! Wrong password.'));
}
return done(null, user);
});
}));
};
console.log('start'); is never called, although passport.authenticate('login' ...) is called.
What can be an issue?
I finally fixed it and everything works. In case anyone face the same issues I'm posting here several problems and solutions.
The req.body was empty in my app.post, because I didn't add body parser. Fixed it with:
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
Username field was empty all the time because I named it as email and passport expected username. Fixed with:
new LocalStrategy({
usernameField: 'email', // this parameter
passReqToCallback: true
}
When doing an async login to passport-local my session never stores a user and the serialization is never called.
Since I'm not redirecting when I'm done logging in (I just notify the user about it through the response to their ajax call) I guess I'll have to trigger the serialization manually when I've logged in? I'm not sure how the session will have the user associated when they refresh though. This is my main concern since my users never leave the main page in my use case, I do however want them to stay logged in for some period of time after they refresh (unless they explicitly log out).
Any ideas how I can cause passport to save the session when I'm logging in with ajax?
Configuration
app.use(require('express-session')({secret:'secret'}));
app.use(passport.initialize());
app.use(passport.session());
app.get('/', function(req, res) {
res.render('index', {
user: req.user // undefined here, although req.session does exist. req.session.passport does not exist.
});
});
app.post('/login', function(req, res, next) {
passport.authenticate('local-login', {}, function(error, user, info) {
if (error) {
return res.status(500).json(error);
}
if (!user) {
return res.status(401).json(info);
}
res.json({user: user});
})(req, res, next);
});
Login call, client side
$.ajax({
url: "/login",
type: "POST",
contentType: "application/json",
data: '{"email":"a#b.c", "password":"abc"}',
cache: false,
timeout: 5000,
success: function(data) {
data.user; // valid, this comes back.
}
});
Serialization (never called)
passport.serializeUser(function(user, done) {
console.log('serialize user:', user); // never called
done(null, user);
});
passport.deserializeUser(function(id, done) {
console.log('deserializeUser user:', user); // never called
User.findById(id, function(err, user) {
console.log('deserializeUser user (done):', user);
done(err, user);
});
});
Strategy
var onLocalLoginStrategy = function(req, email, password, done) {
User.findOne({
'local.email': email.toUpperCase()
}, function(err, user) {
if (err)
return done(err);
if (!user)
return done(null, false, { message:'No user found.'} );
if (!user.validPassword(password))
return done(null, false, { message: 'Incorrect password.' });
return done(null, user);
});
};
I set-up passport on nodejs with mongoose for allowing users to login and create new accounts. Create new account is working but the login part doesn't.
users.js
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var mongoose = require('mongoose');
var User = require('../models/user');
router.get('/login', function(req, res, next) {
res.render('login', {
'title': 'Login'
});
});
passport.serializeUser(function(user, done){done(null, user);});
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/register', failureFlash:'Invalid username or password'}), function(req,res){
console.log('Authentication Successful');
req.flash('success', 'You are logged in');
res.redirect('/');
});
../models/user.js
var mongoose = require('mongoose');
var bcrypt = require('bcrypt');
mongoose.connect('mongodb://localhost/nodeauth');
var db = mongoose.connection;
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, callback);
}
module.exports.createUser = function(newUser, callback){
bcrypt.hash(newUser.password, 10, function(err, hash){
if(err) throw err;
// Set hashed pw
newUser.password = hash;
// Create User
newUser.save(callback);
});
}
If I create the new user than is working the data are uploading the MongoDB but if I try to Log In it just drop me to the /users/register page I do not have any error
Well it is not doing anything because your login route is not calling anything... check the documentation on http://passportjs.org/docs to see how passport works.
In user.js you need something like
router.get('/login',
passport.authenticate('local'),
function(req, res) {
res.render('login', {
'title': 'Login'
});
});
Then in a separate file you need to setup your authentication strategy, for example:
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
Check this tutorial for a step-by-step on implementing local authentication strategy.
http://code.tutsplus.com/tutorials/authenticating-nodejs-applications-with-passport--cms-21619
If you want something more advanced, this code has examples of social authentication strategies (for example, your users can log in using Facebook accounts)
https://github.com/mjhea0/passport-examples
Good luck!