How to properly initialize session data using passport.js having different strategies? - passport.js

I'd like to setup some session data for the user if the user has logged in (authenticated) correctly, for instance, look up user in the database and determine his role, what parts of the app can be seen, etc.
But, I want to use different passport strategies for dev/localhost and for production.
Therefore, I plug in different strategies based on the different ENV settings.
What troubles me is where do I put the common code, for both(any) strategies, the one that is executed (only once!) after a user authenticated correctly.
I don't wont to embed it in the strategies and duplicate the code.
Edited:
Example:
Say I have this code in my 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); }
if (!user.verifyPassword(password)) {
<setup session data code here>
return done(null, false);
}
return done(null, user);
});
}
));
Now, in my other strategy I'd have to repeat <setup session data code here>.
I want to move that part of the code outside of both my strategies, so that strategies would only authenticate users.
I'm looking for some onSuccessfulAuthorization handler/event in the passport to execute that code once the authorization succeeds, regardless of the current strategy used.

There are more than one way to do this. Depending on your situation, you can come up with something that works best for you.
I'd suggest having another file session_setup.js or even common.js if you need more functions. Put your common codes in there as functions, then require them whenever you need it.
session_setup.js:
module.exports.setupSession = function (user) {
// put your common code here
}
And in your strategy file:
let setupSession = require('./session_setup.js');
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); }
if (!user.verifyPassword(password)) {
setupSession(user); // you can pass whatever argument you need there.
return done(null, false);
}
return done(null, user);
});
}
));
If the login in setupSession is asynchronous, you can accept a callback argument as well, or better, return a Promise and in the strategy:
if (!user.verifyPassword(password)) {
setupSession(user).then(() => {
done(null, false);
});
}
It all depends on what you actually do in that piece of code. You get the idea.

Related

passport-jwt: with every request must take unnecessary query to database or not

In paaport-jwt docs is coming:
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({id: jwt_payload.sub}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
// or you could create a new account
}
});
}));
It means by every request from client we create a query to database To get user. (Low Performance. In session we haven't this) In the event that, when user authenticated, we set necessary information to payload of jwt (like UserID and User Role), and user in client can't change jwt.I think that is not necessary. For what not use of this:
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
return done(null, {id: jwt_payload.sub, role: jwt_payload.role});
}));
Does it have any reason?
This is the case where you have to have a tradeoff between security and performance. You are correct that first approach will create an extra query for the database but will be good from the security point of view. As you will not expose the user information on jwt in the first approach. You can use simple tools available in the market to decode the jwt.
If you are not exposing sensitive user information, your second approach works fine.

Where can I find the details about the argument to be passed to a passport.js Strategy?

I am not getting the exact details about which arguments need to be passed in the Passport Strategy. It would be very helpful if someone points me to the documentation.
For example:
passport.use(new LocalStrategy(
function(username, password, done){}));
Here the callback function of the LocalStrategy requires username, password and done.
I just want to know where it is documented and also for BearerStrategy.
If you need to create your own strategy you will want to extend passport-strategy. It can be found here with documentation: https://github.com/jaredhanson/passport-strategy
The parameters will be up to you to define depending on the needs of your custom strategy.
I just want to know where it is document
Passport local <---this shows which arguments need to be passed to configure passport
The local authentication strategy authenticates users using a username
and password. The strategy requires a verify callback, which accepts
these credentials and calls done providing a user.
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); }
if (!user.verifyPassword(password)) { return done(null, false); }
return done(null, user);
});
}
));
You compare the username from the name attribute of your form to the one in your database. I think you were asking for documentation so I provided the link above

Return value from one function to another with Node.JS

I am working on a login interface using the MEAN stack. I have managed to get it to work using PassportJS. My problem now is I need a way to let my client-side know whether the person logging in is an admin or user(user role). These info are available from my MongoDB.
The flow of my API call is as follow :
app.post('/login', passport.authenticate('local'), authRoutes.loginCheck);
First, it runs the passport.authenticate where it calls the function below
function verifyCredentials(username, password, done) // username & password from what user provide when logging in
{
console.log('VC');
User.findOne({username: username}, function(err, user) //query Mongo
{
console.log(user); // User role is available here, in JSON format
if(user === null) // if no username in database, do this
{
console.log('Username does not exist in database');
}
else
{
user.comparePassword(password, function(err, match) // function written to compare hashed password in Mongo & password provided by user
{
if(match)
{
done(null, {id: username, name: username});
return user; // this is not the correct syntax, but the idea is, I want to send over the user details here, so I can access the role later
}
else
{
done(null, null);
}
});
}
});
}
The verifyFunction is called with this syntax.
passport.use(new LocalStrategy(verifyCredentials));
Once that function is successfully called, the server executes the 2nd part of it which is the loginCheck.
module.exports.loginCheck = function(req, res)
{
console.log('Calling loginCheck route');
// I generate some sort of jwt token here
// payload, body, blah blah blah ...
console.log(req.body);
res.json({
authenticated: req.isAuthenticated(), //built-in authentication function, returns true or false
token: token // sends over token
role: user.role // want to send over something like this
}); // sends all these to client side as JSON
}
Since both functions are in different files, I am unclear if I have to require something or simply just pass an extra parameter to the loginCheck function. I have tried the latter though and it did not work.
One way that I could think of is do another Mongo query in the loginCheck function, but that would be kinda redundant.
Even a specific keyword for me to google up would definitely be of big help as don't I know what I should be looking for. The reason is because I am new to NodeJS, thus I am not familiarize with most of the terms yet.
I think these codes should suffice but if I am needed to provide more, let me know and I will do so. Thanks in advance !!
To pass control to next matching route you need to use next that passes as third argument in the routes:
function verifyCredentials(req, res, next) {
User.findOne({username: req.body.username}, function(err, user) //query Mongo
{
if(user === null) {
return next(new Error('Username does not exist in database'));
} else {
user.comparePassword(req.body.password, function(err, match) {
if(match) {
next(null, {id: username, name: username});
} else {
next(new Error('not match'));
}
});
}
});
}
app.post('/login', verifyCredentials, authRoutes.loginCheck);

How to do Authentication with Node.js and MEAN stack?

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.

Express.js/Passport app creating new session for each request, despite session ID in request headers

This has not been noticed before, because for client-server communications, nothing requiring authentication is happening via XHR (as of yet). In implementing some integration tests, I am creating realy requests node-xmlhttprequest to a real instance of the application. User is being authenticated in the first request, and in theory the user's unique identifier and some other pertinent information is stored in session (as I've said, this works just fine for real clients, who do not need to confirm their identity over XHR). For some reason, even though subsequent requests are being fired with the exact same session ID, I am not able to retrieve the session in subsequent requests, and thus can't see that the user is authenticated, and this leads to failing tests where the expected behaviour does not happen, and HTTP 401 UNAUTHORIZED is filling up the terminal.
I've seen a couple of questions which look sort of like this, but that "same session ID" thing does not seem to be present among them.
Do I need to manually add Set-Cookie headers to all XHR requests? That's terrible, there's got to be a better way!
So people like source code, here's some from the test, firing these requests:
// Ensure logged in before sending request, to verify that authorized
// users encounter the expected behaviour.
jQuery.post(domain+'/login', {email:'test#express.app', password:'12345678'},
function(data,x,y){
data.should.equal("true")
jQuery.ajax({url:domain+'/event', type:"POST",
name: "TestEvent", location: "TestVille",
startDate: new Date('2013-09-01'), startTime: '6:45 pm',
description: "Test Event #1", success: state.success,
error: state.error, completed: state.completed
})
})
Sign in happens, user ID gets written into session (`req.logIn() should do this, and it does not report any failures), serialization of the user ID does not report any failures, and I am extremely confused as to why subsequent requests using the same session ID are unable to find the serialized user ID.
I am generally not involved with web development, so this may be obvious to some people, but I've been searching for an answer all day and have simply not been able to find one. I'd appreciate any pointers, and am happy to provide as much code as I'm able to, to illustrate what the problem is.
A few additional points of code which may be pertinent:
Serialization/deserialization of user ID (currently implemented in the simplest possible
manner -- This is very early in the initiallization of middleware, after initiallizing passport and passpot.session(). And this works perfectly well for non-XHR requests)
// Serialize user for passport session-store
// Currently only serializing 'user_id'
passport.serializeUser(function(user, done) {
done(null, user._id)
})
// Deserialize user from session-store to provide
// access to the User instance
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user)
})
})
Authentication of users via the local strategy:
passport.use(new LocalStrategy({
usernameField: "email", passwordField: "password",
passReqToCallback: true, failureFlash: true
},
function(req, email, password, done) {
User.findByEmail(email, function(err, user) {
if(user) {
if(err) console.log(err)
if(user instanceof UserLocal) {
user.verifyPassword(password, function(err, match) {
if(err) console.log(err)
if(!match) {
return done(err, false,
"Username or password is incorrect.")
}
return done(null, user)
})
} else {
var msg = "Account registered under " + user.providerName()
+ ", please login using " + user.providerName()
return done(err, false, msg)
}
} else {
return done(err, false, "Username or password is incorrect.")
}
})
})
And finally the requests which write to session in the first place:
function loginHelper(req, res, next) {
passport.authenticate('local', {
failureFlash:true,
failureRedirect: false,
successRedirect: false
},
function(err, user, info) {
req.logIn(user, function(err) {
if(!err) err = {}
if(req.xhr) {
console.log(req.session)
res.status(200).end(err.message || "true")
} else {
if(err.message) req.flash('error', err.message)
else res.redirect('/')
}
})
})(req, res, next)
}
I know there are some weird things like sending a status of 200 regardless of the login status, but I can confirm that the serialized user id is written to session on the initial login XHR request, and that it's not deserialized on subsequent XHR requests.
As I believe I mentioned, I am relatively inexperienced in this area, and could very much use a boost. Any assistance whatsoever would be much appreciated, thanks in advance.
That's help me
First
app.use(express.static(__dirname + '/public', { maxAge: oneDay }));
and update functions to do not trigger database
passport.serializeUser( (user, done) => {
var sessionUser = { id: user.dataValues.id, fio: user.dataValues.fio, email: user.dataValues.localemail, role: user.dataValues.role, login: user.dataValues.login, position: user.dataValues.position }
done(null, sessionUser)
})
passport.deserializeUser( (sessionUser, done) => {
// The sessionUser object is different from the user mongoose collection
// it's actually req.session.passport.user and comes from the session collection
done(null, sessionUser)
})
https://www.airpair.com/express/posts/expressjs-and-passportjs-sessions-deep-dive

Resources