Similar to the google login https://accounts.google.com/ServiceLogin when you check "stay signed in" log in then log out, you have the email field filled out as well as your image.
I think this is a cool UI feature to have, so I implemented https://github.com/jaredhanson/passport-remember-me but the cookie is only available when logged in, logging out destroys the cookie.
So messing around I did this
// when the user logs in
User.findOne({ _id: req.user.id }, function (err, user) {
user.save(function(err) {
if(err)
console.log('error');
else
res.render('app', { user: req.user });
res.cookie('testCookie', req.user.local.email, { maxAge: 900000, httpOnly: true });
});
});
I will need validation and only set the cookie if req.body.remember_me is true, but for now this sets a cookie like this
Then when I render the login page I do this
res.render('login', { user: req.cookies.testCookie });
Obviously I don't think this is good, but I am confused what is the purpose of https://github.com/jaredhanson/passport-remember-me if I can't access data for the logged out user?
Looking at googles cookies they don't expose the email like I did it looks like this
Looking at it there is an ID=b1051bd.
So IDK any help would be great, I am trying to remember the user that checked the remember me box when logged out.
Related
I've written code with Passport.js for authentication purpose. While user logged into chrome and using same credentials user logged into another browser 'FF'.
As we all know that Passport.js store all details into req.users and req.session.passport.users. If from one browser user update some details how can we update into another browsers req object without logout?
Same kind of, If admin update user1 details and he already logged in than how that will affect?
Any clue?
As we all know that Passport.js store all details into req.users and
Not necessarily. passport.js does not store user details in req.user, but your passport.js integration code loads the user details from some backend storage and then puts it in the request object on every request.
So it is up to you to update the user in the backend and decide when to retrieve a new version ( instead of just deserializing jwt, for example ) on every request just as well.
Sample code from http://www.passportjs.org/docs/basic-digest/
passport.use(new BasicStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (!user.validPassword(password)) { return done(null, false); }
return done(null, user);
});
}
));
This code is executed on every single request which means that on every request to the server your user is loaded from your database.
Even if you're working with multiple sessions in multiple browsers the result is the same. So it is up to you to handle when and how you want to update your user in your database.
Otherwise if you don't load your user from an external datasource but e.g. deserialize the whole user object from jwt ( which is not recommended unless you really understand what you're doing ) then you need to think of a synchronisation strategy e.g. check some updated flag in db or some cache on deserialization
I'm building an application using Node that uses Passport.js to handle user login using a local database.
So I have the following code that gets called when a user goes to /profile. After successfully logging in the user gets redirected to /profile. Which does happen according to morgan.
app.get('/profile', passport.authenticate('local-login', { session : false, failureRedirect : '/login' }), function(req, res) {
console.log("testnow");
res.render('profile.ejs', {
user : req.user // get the user out of session and pass to template
});
});
My local-login code is the following.
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.')); // 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
console.log("testdone");
return done(null, user);
});
}));
When testing the code I login and get redirected to profile for a split second. The console prints "testdone" which is in my local-login code BUT doesn't print "testnow" as it is expected to. Meaning the second function in my /profile get method never seems to get called even tho local-login is calling the next function.
So from the end users standpoint you login (behind the scenes you get redirected to /profile for a split section) and /profile redirects you back to /login.
Any ideas on how to fix this so the second function in my /profile get method actually gets called?
Thanks so much in advance. I would also be more then happy to provide any additional information to help figure this out.
passport.authenticate() is meant to handle the actual authentication; in other words, to take the login credentials and pass them to the strategy. It's not meant to pass along requests if they are already authenticated, which is what you're trying to use it for.
Instead, you want to use something like connect-ensure-login to guard routes for which a user has to be logged in.
See also this Passport example project.
Currently I am checking whether or not a user has a certain domain name email address using the following code. Initially, when the google authentication window shows, there is no option for the user to change their email address if they are already logged into Chrome. When I log into similar sites with Google authentication, I seemingly recalled being allowed to add an email address, or something of that nature.
So, say that the user attempts to log on with a non-lsmsa.edu email address and it fails. Currently it displays a nasty error. How would I make it such that the user is allowed to attempt to re-login with a different email address.
if ( profile.emails[0].value.indexOf("lsmsa.edu") > -1 ) {
var newUser = new User()
newUser.google.id = profile.id
newUser.google.token = token
newUser.google.name = profile.displayName
newUser.google.email = profile.emails[0].value
newUser.save(function(err) {
if (err) throw err
return done(null, newUser)
})
}
else {
done(new Error("Invalid Domain. Must use LSMSA email address."))
}
Check out the hd parameter. It makes sure that the user can only sign in with a proper email.
EDIT: This isn't a per-request option. If you want to use it with passport-google-oauth, edit your config to be like this:
passport.use(new GoogleStrategy({
returnURL: 'http://www.example.com/auth/google/return',
realm: 'http://www.example.com/',
// Add this
hd: 'example.com'
},
function(identifier, profile, done) {
// Blah Blah Blah, Blow up Pluto, Milk Cows, Eat Chocolate, Etc.
}
));
EDIT: If for some reason you have to have them login again, instead of using hd, just destroy the session (req.session.destroy();), then redirect them to your authentication url (ie. /auth/google). However, using hd will be a much nicer user experience.
Using Passport.js in Node, is there a way for me to allow one user to impersonate another? eg. as an Administrator in the application, I want to be able to log in as another user, without knowing their password.
Most simply, I would be satisfied if I could change the serialized user data (user ID) so when deserializeUser is called it will just assume the identity of the alternate user. I've tried replacing the value at req._passport.session.user and the value at req.session.passport.user but the net effect is just that my session seems to become invalid and Passport logs me out.
Passport provides a req.logIn method in case you want to do the authentication manually. You can use it to login any user even regardless of authentication.
Here's how you can use it. Have the Admin login normally, who'll have an isAdmin flag set.
Then place a middleware before passport.authenticate in your login route. This will login the new user based only on their username, if the currently logged in user isAdmin.
app.post('/login',
function forceLogin(req, res, next) {
if (!req.user.isAdmin) return next(); // skip if not admin
User.findOne({
username: req.body.username // < entered username
}, function(err, user) {
// no checking for password
req.logIn(user);
res.redirect('/users/' + user.username);
});
},
passport.authenticate('local'),
function(req, res) {
res.redirect('/users/' + req.user.username);
}
);
I have another way to impersonate, because:
I didn't want to mess with internals of authentication/passport like
session storage / logIn / etc. You must understand them really well
and also they are prone to change so I'd say it's not an option for
me.
Also, I'd like to still be able to tell if action is made from
superuser (impersonated) or normal user (not impersonated).
What I do is:
Have a route for user with superadmin role to impersonate, like /superadmin/impersonate?username=normaluser1 which sets req.user.impersonated.userid = normaluser1.userid
Then I have a middleware, which checks if user is superadmin and is impersonated:
if (req.user.isAdmin && req.user.impersonated) {
req.user.userid = req.user.impersonated.userid;
}
Also, I have found this to be a good article about user impersonation. Similar to my approach, and good for inspiration for building something similar.
The answer to your question is basically: no. The reason is this: the sessions library that is being used 99% of the time is signing the cookies, so if you tamper with the data the web server will reject it.
The way around this is to write your own passport authentication strategy that obviously doesn't do this, but I'm assuming you're talking about working with the built-in strategies here.
I have an Backbone View which sends an Ajax call to the server to remove a session.
On the server following event is triggered:
app.delete('/session', function(req, res) {
if (req.session) {
req.session.destroy(function() {
res.clearCookie('connect.sid', { path: '/' });
res.send('removed session', 200);
});
} else {
res.send('no session assigned', 500);
}
});
The weird about this is that I can press the logout button multiple times without getting a HTTP 500 error code. Also chromium shows me that a cookie still exists.
What is going wrong?
Regards
EDIT:
I found out that this isn't directly a session issue but a cookie one.
I added res.clearCookie to the route. Unfortunatly the behaviour (cookie, session keep alive) didn't change
EDIT2:
I now gave res.clearCookie some parameters => res.clearCookie('connect.sid', { path: '/' });
Now at least the cookie is gone in the browser. But the session seems to be still available.
Or at least I can call the logout route how often I want even req.session should be false
EDIT3:
I now removed all sessions out of redis and restarted everything (redis, node, browser).
Than I have logged in again and logged out. This works so far but when I relaod the page with F5 I get a new session. WHY?
To concentrate all comments together I have written an answer:
Because express always creates a session and a cookie for a client we have to take a different approach than just to check if there is a session.
This parts handles logins
app.post('/session', function(req, res) {
User.findOne({ username: req.body.username })
.select('salt') // my mongoose schema doesn't fetches salt
.select('password') // and password by default
.exec(function(err, user) {
if (err || user === null) throw err; // awful error handling here
// mongoose schema methods which checks if the sent credentials
// are equal to the hashed password (allows callback)
user.hasEqualPassword(req.body.password, function(hasEqualPassword) {
if (hasEqualPassword) {
// if the password matches we do this:
req.session.authenticated = true; // flag the session, all logged-in check now check if authenticated is true (this is required for the secured-area-check-middleware)
req.session.user = user; // this is optionally. I have done this because I want to have the user credentials available
// another benefit of storing the user instance in the session is
// that we can gain from the speed of redis. If the user logs out we would have to save the user instance in the session (didn't tried this)
res.send(200); // sent the client that everything gone ok
} else {
res.send("wrong password", 500); // tells the client that the password was wrong (on production sys you want to hide what gone wronge)
}
});
});
});
That was the login part lets go to the logout:
app.delete('/session', function(req, res) {
// here is our security check
// if you use a isAuthenticated-middleware you could make this shorter
if (req.session.authenticated) {
// this destroys the current session (not really necessary because you get a new one
req.session.destroy(function() {
// if you don't want destroy the whole session, because you anyway get a new one you also could just change the flags and remove the private informations
// req.session.user.save(callback(err, user)) // didn't checked this
//delete req.session.user; // remove credentials
//req.session.authenticated = false; // set flag
//res.clearCookie('connect.sid', { path: '/' }); // see comments above res.send('removed session', 200); // tell the client everything went well
});
} else {
res.send('cant remove public session', 500); // public sessions don't containt sensible information so we leave them
}
});
Hope this helps