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();
});
Related
I want to update user's ip every time he or she logs in.
I was informed that only middlewares get req, res, and next parameters, but I am using passportjs, which should come first to authorize the user. How should I update user data?
Below is my code block of passport.js using Basic Strategy.
passport.use(new BasicStrategy(
function(username, password, callback){
User.findOne({username:username}, function(err,user){
if(err){return callback(err);}
//no user found with the email
if(!user){return callback(null, false);}
user.verifyPassword(password, function(err,isMatch){
if(err){ return callback(err);}
// password did not match
if(!isMatch){return callback(null,false);}
//success
// UPDATE USER INFO
user.token = jwt.sign(user.email+Date.now(), "testtoken");//should change later
user.last_login = Date.now();
// I AM TRYING TO UPDATE THIS WITH req.ip
user.last_ip = req.ip;
user.save(function(err, user1){
if(err) return callback(err);
return callback(null,user);
});
});
});
}
));
You can configure BasicStrategy to pass the request as first argument:
passport.use(
new BasicStrategy({ passReqToCallback : true }, function(req, username, password, callback) {
...
})
);
This (sadly) isn't well documented, but I believe most Passport strategies support it (see also).
I'm using the Node.js passport module (local Strategy) and within my isLoggedIn method, I want to check if I'm using the development environment. If I am, then I want to just log myself in with the admin user account, if not, then it should redirect to the login page where a normal user would login as usual.
The reason for this, is that during development, I have to keep re-logging in over and over again every time I make a change to the code which is really time consuming.
Here's my code (some parts are removed for clarity)
index.js
require('./app/routes.js')(app, passport);
app/routes.js
module.exports = function(app, passport) {
app.post('/search', isLoggedIn, function(req, res) {
// redirect to search page etc...
});
}
function isLoggedIn(req, res, next) {
// I want to check here if I'm it's the development environment and not production
// If it's development, then it should perform a database lookup and look up the
// admin user's account, otherwise it should carry on and use the isAuthenticated
// method below.
// I wanted to use app.get('env') but "app" isn't available here..
// if user is authenticated in the session, carry on
if (req.isAuthenticated())
return next();
// if they aren't redirect them to the home page
res.redirect('/login');
}
config/passport.js
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 back the entire request to the callback
},
function(req, email, password, done) { // callback with email and password from our form
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'local.email' : email }, function(err, user) {
// if there are any errors, return the error before anything else
if (err)
return done(err);
// if no user is found, return the message
if (!user)
return done(null, false, req.flash('loginMessage', 'No user found with username \'' + email + '\'')); // req.flash is the way to set flashdata using connect-flash
// if the user is found but the password is wrong
if (!user.validPassword(password))
return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.')); // create the loginMessage and save it to session as flashdata
// all is well, return successful user
return done(null, user);
});
}));
Move the isLoggedIn function within the module.exports definition. The function will then have access to the scoped app object and isLoggedIn will remain "private" to the outside consumer (since it doesn't return anything).
module.exports = function(app, passport) {
app.post('/search', isLoggedIn, function(req, res) {
// redirect to search page etc...
});
function isLoggedIn(req, res, next) {
app.get('env');
// ...
}
};
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'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);
});
});
I am currently working on a text based game with a small team of developers. The game requires login and we are using the MEAN (MongoDB, Express, Angular, Node) Stack for the application codebase, however i am stuck on authentication, as a rails developer i am used to being able to drop in a gem and use the helpers available.
has anybody has any experience with MEAN and Authentication?
the MEAN stack by linnovate uses Passport.js for its authentication. Passport uses different strategies for authentication. One of these strategies is a username and password pair, which they call LocalStrategy.
Here is one of the samples from the Passportjs-Local Github Examples Page
Step 1: Require Passport
First you require the module after doing npm install passport
var passport = require('passport');
Step 2: Configure 'Verify' Function
Use the LocalStrategy within Passport. Strategies in passport require a verify function, which accept credentials (in this case, a username and password), and invoke a callback with a user object. In the real world, this would query a database; however, in this example we are using a baked-in set of users.
passport.use(new LocalStrategy(
function(username, password, done) {
// Find the user by username. If there is no user with the given
// username, or the password is not correct, set the user to `false` to
// indicate failure and set a flash message. Otherwise, return the
// authenticated `user`.
findByUsername(username, function(err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Unknown user ' + username });
}
if (user.password != password) {
return done(null, false, { message: 'Invalid password' });
}
return done(null, user);
})
});
}
));
Step 3: Initialize Passport on app
You need to tell Express that you will be using passport and that it will be managing sessions for you. This is done by using the app.use() during app configuration.
app.use(passport.initialize());
app.use(passport.session());
Step 4: Configure Middleware on the login URI
Next we need to create a method that will accept when a user tries to login to the app using by POST-ing to a specific URI. It will look like this.
// POST /login
// Use passport.authenticate() as route middleware to authenticate the
// request. If authentication fails, the user will be redirected back to the
// login page. Otherwise, the primary route function function will be called,
// which, in this example, will redirect the user to the home page.
//
// curl -v -d "username=bob&password=secret" http://127.0.0.1:3000/login
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login', failureFlash: true }),
function(req, res) {
res.redirect('/');
});
Step 5: Set up Sessions
You may have to create your own serialization for User objects that are being stored in the sessions. That is done with the following
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing.
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
findById(id, function (err, user) {
done(err, user);
});
});
You can have a look at http://meanjs.org/
They have a very solid integration of passport.js strategies.
Especally useful is the implementation of Salt and Crypto-Technies to make the integration safe. Search for Salz within the repo.
See
https://github.com/meanjs/mean/blob/master/modules/users/server/config/strategies/local.js
For serialization and deserialization.
Or if you'd prefer a custom implementation, I recently posted a complete MEAN Stack User Registration and Login Example
Here's the snippet from the user service that handles authentication:
function authenticate(username, password) {
var deferred = Q.defer();
usersDb.findOne({ username: username }, function (err, user) {
if (err) deferred.reject(err);
if (user && bcrypt.compareSync(password, user.hash)) {
// authentication successful
deferred.resolve(jwt.sign({ sub: user._id }, config.secret));
} else {
// authentication failed
deferred.resolve();
}
});
return deferred.promise;
}
Or use mean.io which has user management out of the box.