Questions about how passport.js works. Specifically about user.id - javascript

I have no id field in my model ,doc, or record or whatever you want to call it. So how come done(null, user.id) works. I have an _id but no id. It looks like a passport object is being added to my session with the _id of the user in the DB, but how did it get the _id is it done automatically? in the docs it says
In this example, only the user ID is serialized to the session
how did it get the id and how did it get the user?
similar example in my code:
passport.serializeUser(function(user, done){
done(null, user.id)
});
passport.deserializeUser(function(id, done){
User.findById(id, function(err, user){
done(err, user);
})
});
What are the steps that it takes to get the user id?
I also have
passport.use("login", 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 : "no User has that name"})
}
.......
Does the fact that I use passport.use() somehow connect the user in the DB to the passport methods.
Edit lets say I want to transfer the doc (user) via passports on the name field should I use user.name?
Also I'm not really sure about how serializing works

So how come done(null, user.id) works. I have an _id but no id.
You're using Mongoose, which adds a so-called virtual getter called id to documents, that returns the _id property. So in your case, both user.id and user._id will work (and will return the same).
Does the fact that I use passport.use() somehow connect the user in the DB to the passport methods.
Yes and no. The strategy that you pass to passport.use() implements the authentication process. You have to implement how the entered user information is validated against your database. In the example code, a call to User.findOne() is made, and the result of that call is checked against the supplied information ("Is username a valid username? If so, is password the password that belongs to this user?").
If the username and password match, the done callback is called with the user document as an argument. This document is what Passport will pass around to various other parts, for instance, to passport.serializeUser() (see below).
Also I'm not really sure about how serializing works.
Serializing is used to store information about the user in a "session object". This object should be able to uniquely identify the user. And since user.id (or user._id) is unique to a user, it's a good property to use.
That's where passport.serializeUser() comes in: it gets passed the user document, and it should provide Passport (by calling the callback function) a uniquely identifying piece of information about that user. Hence done(null, user.id).
The session object itself is also uniquely identified by a "session key". This key is stored in a HTTP cookie and sent to the browser when a user logged in successfully. When the browser opens another page on your website, it sends along that cookie, and using the session key in the cookie, the session object is retrieved from the "session store".
The session object contains the unique user id, but not the rest of the user information. So that's where passport.deserializeUser() comes in: it gets passed the id, and performs a database lookup to retrieve the full user document belonging to that id. This is what Passport will make available as req.user in your route handlers.
If all these steps are executed successfully (which isn't always the case, because cookies can expire, users can get deleted, etc), it will mean that Passport was able to positively identify the user, and they don't have to log in again. At least not until the session expires.

Related

Passport.js, Express session Cross-browser with same credentials

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

Node.js Passport not calling next function

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.

Parse - why does user.getSessionToken() return undefined

I'm using cloud function to fetch user's sessionToken. The documentation says, this method only returns correct value when a user object is fetched with the master key. However, even if I use the master key, I still get the undefined result. What's wrong with my code?
Parse.Cloud.define("hello", function(request, response) {
Parse.Cloud.useMasterKey();
Parse.Promise.as().then(function(){
var query = new Parse.Query(Parse.User);
return query.first({useMasterKey:true});
}).then(function(user){
return user.fetch({useMasterKey:true});
}).then(function(user){
response.success(user.getSessionToken());
});
});
That won't work because the user is not logged in when you fetch him via cloud code. For getSessionToken to work you need to already have a logged in user. Because, otherwise how would CloudCode know which session of your user you want to get? (if he is logged in multiple devices) Also your code is returning the first user in the database, is that realy what you want?
Either way, you need to log the user in via cloud code and then return the session token. However I had some trouble doing that without the password (just by calling user.logIn(), it should work so give it a try) so the only solution I found was to change the password of the user and then log him in. Why do you need to fetch the user's session token? If you don't mind changing the user's password you can do something like:
var password; //create new random passowrd
query.first({useMasterKey: true}).then(function(user){
user.set("password", password);
return user.save();
}).then(function(user){
return Parse.User.logIn(user.get("username"), password);
}).then(function(user){
return user.getSessionToken();
});

How can I impersonate another user with Passport.js in Node?

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.

how to store signup data into redis with node.js

i want to store signup data it contains name and email and password.
i will store this data in mongodb like this
db.save({"name":"xxxx","email":"xxxxx","password":'xxxxxxxx'},function(err,result){});
when user login ,they surely give their email id or username with password so i will find this user exist in db or not by using like this
db.find({'email:'xxxxxxx','password':'xxxxxxxxx'},function(err,result){});
i have tried to do same in redis,by like this
db.hmset('key' name xxxxx email xxxx pass xxxxxx,function(){});
it is stored but how can i check email id usename already exist becz user will give email and password only.if i know key then only i can find that data.even if i know key i can get only data i could not be found data already exist ot not like mongodb
how can i solve this?
You could store your users both in a Set and a Hash for details.
You can then check in the Set if a user exists with: http://redis.io/commands/sismember
I think you should break things down into chunks instead of trying to do everything with one query. So, for example, to add a new user, first check if the user exists:
(My example assumes a Mongoose User Model has been defined)
User.findOne({$or : [{'email': req.body.email}, {'username': req.body.username}],
function(err, result) {
if (err) {next(err);}
if (result) {
// you found an existing user
res.send(309, {'message':'Error: User exists'});
} else {
// no user found
var user = new User({
'email': req.body.email,
'username': req.body.username,
'password': req.body.password,
'and-so-on': req.body.moredata
});
user.save(function(err){
if (err) {next(err);}
res.send(200, {'message':'User Registered Successfully'});
});
}
Honestly though, I wouldn't recommend writing a user authentication system from scratch, because it is pretty limiting in todays world of auth methods. I personally use Passport, because it gives you the ability to use multiple auth systems with your app, including Facebook, Twitter, and so on and so forth.

Categories

Resources