I'm using passportjs in order to log user and I try to redirect them after the password verify is complete with angularjs.
But I keep getting "Cannot read property 'name' of undefined" when I try to get user data on another page
Snippet:
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, usr, info) {
res.locals.user = req.usr;
res.json({msg:true});
return next();
})(req, res, next);
});
And somewhere else I try to do something like this:
user.find({name: req.user.name },function(err,q){
Which fire the error "Cannot read property 'name' of undefined"
You have to provide passport with serializeUser and deserializeUser functions in order for passport to store the user in the request object. For more info, check the guide:
http://passportjs.org/guide/configure/
Specifically, look at the bottom section on Sessions. Also, consult this similar question:
Do I implement serialize and deserialize NodesJS + Passport + RedisStore?
In your case, it looks like you're using name instead of id to identify users, so wherever you configure passport, you will probably want to do something like:
passport.serializeUser(function(user, done) {
done(null, user.name);
});
passport.deserializeUser(function(name, done) {
user.find({name: req.user.name }, function(err, user){
done(err, user);
});
});
Related
app.use(passport.initialize());
app.use(passport.session());
app.use(function(req, res, next) {
res.locals.login = req.isAuthenticated();
res.locals.user = req.user;
console.log(res.locals.user);
next();
});
app.use('/', indexRouter);
Here above is the code i used to set my user value in the res.locals.user field.
The console gives me:
{
_id: 5fc3e49c0bfce754c8f923c9,
email: 'myemailadres#hotmail.com',
name: 'Jarne',
password: 'passwordhash......',
__v: 0
}
In my handle bars i'm now trying to use this user variable.
{{#if login}}
welkom
{{user.name}}
TESTER
{{tester}}
This doesn't work. This stays empty..
I can use {{user}} but this give me back the Json format.
How can i access the fields like name, email,... saw some examples in EJS with user.name, user._id but this doesn't seem to work in my handlebars.
Also tried to do this via the router. but same result with tester (tester.name doesn't show anything).
router.get('/', function (req, res, next) {
res.render('index', {
title: '---- Dashboard---- HOME',
tester: req.user
});
});
I also tried to set a res.locals.username = req.user.name
But this resulted in an error.
I found following solution:
{{#each user}}
{{name}}
{{_id}}
{{email}}
{{/each}}
Looks like i needed to loop through the user object.
In my case when I printed out {{user}} in navigation.hbs it showed the JSON string instead of object
This happened because I used a mongo version that requires lean() function after searching the user
add .lean function
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
}).lean()
})
hope it helps!
I have a nodeJS application using express and passport. Im trying to handle the post requests to passport for a login. Im getting an unknown error in my server console: POST /signup 500
First question, how do I debug this? is there any way for me to get more information about why its 500'ing?
Second question, any idea's why its not working from a quick glance of what I have below. I know its not all the code, but maybe there is something obvious..?
app.js:
..
var passport = require('passport');
require('./config/passport')(passport);
..
var routes = require('./routes/index')(app, passport);
..
Passport.js defines my handling of users and the database. It defines a localStragery called local-signup.
/routes/index.js:
module.exports = function(app, passport) {
..
var express = require('express');
var router = express.Router();
..
router.post('/signup',
passport.authenticate('local-signup', {
successRedirect : '/profile',
failureRedirect : '/signup' })
);
..
return router
}
Update:
My code is actually working, it is putting new users in the database, its just the application is returning 500. Using loads of console.log() and trial and error I have tied it down to what i returning from my passport stratagy. I have the following code wich is being hit:
..
} else {
console.log("-] Email is not found, creating new user");
user = {};
user['email'] = email;
user['password'] = bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
console.log("-] Inserting into db");
userCollection.insert( user, function(err, user) {
if (err) {
console.log("Error: " + err );
console.log("records: " + user );
throw err;
}
console.log("returned user: " + user)
return done(null, user);
its something to do with me returning user. If I get rid of this line and just return instead, it works fine but the call to authenticate thinks it has failed and follow that's redirect rather than the one for success. The issue is that i got this code from an application that makes use of mongoose which wraps the database stuff in an object. I don't want to do that, i want to make the db calls directly myself like the code above.
Any idea's?
You're not making use of the error handling in the API.
Try something like this instead:
passport.authenticate('local-signup', {
successRedirect: '/profile',
failureRedirect: '/login',
failureFlash: true })
... this tells passport to 'flash' any error resulting from the callback (assuming there's one defined as part of the verify stage of local-signup).
Now, assuming local-signup in your application is set up like so:
var local-signup = require('passport-local').Strategy;
... you should be able to sort the error handling pretty easily. Here's a sample from the configuration docs at http://passportjs.org/guide/configure/ -- note how errors are returned:
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);
});
}
));
For me the solution was to not use:
passport.authenticate('local-signup', {
But:
passport.authenticate('local', {
This is the default if you do not set a name when registering your strategy like so:
passport.use(new LocalStrategy({
You can pass a name in if you have multiple LocalStrategies:
passport.use('local-signup', new LocalStrategy({
I am learning node and express. I am trying to build a very basic app that will simply let a user log in using json. Then will maintain session until they log out. With asp.net this is a doddle you just set it up in the config and call...
Auth.Login(username,pasword)
When they log out you just do:
Auth.logout()
And if you need to check if they are logged in you simply do:
Auth.IsLoggedIn()
Or code to that effect. Well seems like Passport for node is just not that simple. I spent all night getting this working...
app.post('/authentication/login', function handleLocalAuthentication(req, res, next) {
passport.authenticate('local', function(err, user, info) {
// Manually establish the session...
req.login({username:'me#me.com',password:'password'}, function(err) {
if (err) return next(err);
return res.json({
message: 'user authenticated'
});
});
})(req, res, next);
});
app.get('/authentication/isauthenticated',function(req,res){
console.log(req.isAuthenticated());
})
passport.use(new LocalStrategy(
function(username, password, done) {
return done(null, {username:'ss',password:'sffds'});
}
));
So now I have no cookies, no session persisted when I login and then hit the /authentication/isAuthenticated url. I can't even get a breakpoint to stop in the strategy...
passport.use(new LocalStrategy(
function(username, password, done) {
console.log('ggg');
return done(null, {username:'ss',password:'sffds'});
}
));
Am I looking at the wrong solution for this? Should I be rolling my own auth or something with a basic middleware function?
Check out this tutorial. It's really great and it helped me a lot.
And here's my repo which has implemented passport authentication with users stored in mongodb through mongoose, and hashed passwords. Clone it or just check it out, it should help.
https://github.com/thyforhtian/auth_base.
I have implemented Passport with passport-local and MongoDB and it is working nicely.
However this is a pure client-side single-loading app and so node is not responsible for the rendering of html. So currently I show a loading a spinner on app load and make a separate call to an api to determine if the user is logged in to conditionally render some stuff:
router.get('/me', function (req, res) {
res.send(req.isAuthenticated() ? {} || 401);
});
Since passport already authenticates my routes and calls deserializeUser this seems pointless - I need a way to pass an extra piece of info (in the cookie?) stating that the user is authed, I am guessing in deserializeUser?
server.use(session({secret: settings.sessionSecret}));
server.use(passport.initialize());
server.use(passport.session());
....
passport.use(new LocalStrategy(
localOpts,
function(email, password, done) {
User.findOne({
email: email,
activated: true
}, function (err, user) {
....
});
}
));
passport.serializeUser(function (user, done) {
done(null, user._id);
});
passport.deserializeUser(function (id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
Note that the two cookies that get created when sign in is successful:
express:sess
express:sess.sig
When it detects the presence of these cookies it seems to just call deserializeUser hence why I think I could possibly communicate to the client the user is authed there, or otherwise on sign in inside passport.use middleware?
It turns out that I can simply add a middleware after the passport.session. I was concerned that req.isAuthenticated would fire off another query to the db but it doesn't:
server.use(function(req, res, next) {
res.cookie('isAuthenticated', req.isAuthenticated());
next();
});
I've successfully implemented passport-local into my Express/Mongoose web-app but I'm having trouble figuring out how to render a failed login message properly.
Here's my login route:
app.get('/login', function(req, res) {
res.render('user/login', {
});
});
With a route like that how am I supposed to report an invalid login? If the login is successful it will write the id/username to the req.user object but that doesn't help me in the "GET /login" route because if it's successful you will get redirected to the page you want to go.
That means req.user will always be undefined when you GET the login page.
I want to be able to write out a message saying something like 'yo, invalid login!' when the following things happen:
The user does not exist.
The password supplied does not match but the user existed.
I might want to output a different message depending on what occurred.
When I implemented the LocalStrategy I used this code:
passport.use(new LocalStrategy({
usernameField: 'email'
},
function(email, password, fn) {
User.findOne({'login.email': email}, function(err, user) {
// Error was thrown.
if (err) {
return fn(err);
}
// User does not exist.
if (!user) {
return fn(null, false);
}
// Passwords do not match.
if (user.login.password != utility.encryptString(user.login.salt + password)) {
return fn(null, false);
}
// Everything is good.
return fn(null, user);
});
}
));
As you can see there are some problems but this is how the author of PassportJS set up his application. How are we supposed to access what the Strategy returns?
Like if it throws an error, what am I supposed to even call to get access to err?
Thanks.
In the latest version of Passport, I've added support for flash messages, which make it easy to do what you are asking.
You can now supply a third argument to done, which can include a failure message. For example:
if (user.login.password != utility.encryptString(user.login.salt + password)) {
return fn(null, false, { message: 'yo, invalid login!' });
}
Then, set failureFlash to true as an option to authenticate().
passport.authenticate('local', { successRedirect: '/',
failureRedirect: '/login',
failureFlash: true });
In this case, if authentication fails, the message will be set in the flash, ready for you to render it when you display the login page again.
Custom callbacks are also perfectly fine. The built in options just make it simpler to accomplish common tasks.
Also, I'm curious: you mention that there are problems with the sample. What do you think should be improved? I want to make the examples as good as possible. Thanks!
(For more details, see this comment on issue #12).
You can use the custom callback or middleware functionality to have more control. See the Authentication section of the guide for examples.
For example, a custom callback might look like:
app.get('/login', function(req,res,next) {
passport.authenticate('local', function(err,user) {
if(!user) res.send('Sorry, you\'re not logged in correctly.');
if(user) res.send('Skeet skeet!');
})(req,res,next);
});
Alternatively, you could always redirect both responses:
app.get('/login',
passport.authenticate('local', { successRedirect: '/winner',
failureRedirect:'/loser' }));
Or redirect the failure with simple middleware:
app.get('/login', ensureAuthenticated,
function(req,res) {
// successful auth
// do something for-the-win
}
);
// reusable middleware
function ensureAuthenticated(req,res,next) {
if(req.isAuthenticated()) {return next();}
res.redirect('/login/again'); // if failed...
}