and thank you for viewing this question.
I'm building a service with 3 express apps using vhost:
For client
For moderator
For admin (this one not developed yet)
So each one of them uses Passport.js with diffetent authentication (diffrent user collections). End every one of them successfully authenticates. The problem is in Passport's serialize() and deserialize() method which overrides previous in another vhost. I call them this way:
server.js
var app = express(),
clientApp = require('./config/client/express')(db),
moderatorApp = require('./config/moderator/express')(db);
app.use(vhost('localhost', mainApp));
app.use(vhost('moderator.localhost', moderatorApp));
app.listen(config.port);
client/express.js
// some express settings, router settings
// and here I set passport
passport.serializeUser(function(user, done) {
done(null, user.id);
});
// Deserialize sessions
passport.deserializeUser(function(id, done) {
User.findOne({
//....
})
});
// Require necessary strategies
require(path.resolve('config/moderator/strategies/local.js'))();
// Return Express server instance
return app;
moderator/express.js
here is almost (made some security stuff) same as in client/express.js except Passport's deserialize() method. It looks for a user in SuperUsers collection.
FINALLY ABOUT PROBLEM
The problem is after authentication passport can't deserialize user from needed collection. I think it overrides previous passport deserialize settings. But if leave only one passport serialize and deserialize method (delete another serialize & deserialize method which overrides prvious) it work totally OK.
THE QUESTION
How can I set passport's serialize and deserialize method's in two vhosts that it won't conflict with each other???
Please community help me.
Related
Using passport in an express app. For reasons, the session tokens expire after one hour.
If the user is active when the session expired, the deserialize function "fails", i.e., user is undefined.
passport.deserializeUser(function (id, done) {
const user = sessionManager.userLookup(id);
done(null, user);
});
The trouble is that when user is undefined, then there is no req.user for subsequent middleware. So to the code it simply appears that the user is not signed in, with no breadcrumbs to indicate that the session just expired. The app simply redirects all request from unauthenticated users to /login.
For a user in the middle of a workflow, this experience is sub-optimal.
The expiration can be detected within passport.deserializeUser() like this:
passport.deserializeUser(function (id, done) {
const user = sessionManager.userLookup(id);
const errorInfo = ( expire logic check ) ? 'session has expired' : null;
done(errorInfo, user);
});
I can get the logic check right with the sessionManager. The trouble with this solution is that passport sends the user a 500 Internal Server Error, which is also sub-optimal.
What I would like is for the app to send a flash error saying the session has expired. But passport.deserialize() has no visibility to the req object for calling req.flash().
At this point the only way I can think to resolve the issue is to insert a middleware before passport, where the code would lookup the user in the session manager and call req.flash() if the session has expired. It seems like passport should provide a better way to handle such errors.
Answers would be extra-helpful if they include a link to documentation for passport.deserialize(). The only docs I have found here make no mention of how passport handles errors or if it is possible to configure or override the behavior.
UPDATE
After some reflection, flash is not the best mechanism for reporting the session expiration. The app should instead redirect to a "session expired" page. However, the main question still stands. The call changes from req.flash() to res.redirect(), but neither of these objects is available in passport.deserialize().
you can add req as a first parameter in the function, like this:
(also, recommend you use arrow function)
passport.deserializeUser(async (req, id, done) => {
req.flash('error', {});
(...)
});
I am working on setting up logins with session using Express, express-session, and passport.
In a passport documentation as I go I see this example: http://www.passportjs.org/docs/profile/
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
Also, I was following this tutorial: https://github.com/bradtraversy/node_passport_login
Now according to these examples deserializeUser stores the user information in req.user. However, I see that the whole user is stored which puzzles me, as this also stores the passwords to the object.
Isn't that risky?
Or maybe it is not possible to access req.user from front end?
Assuming your backend server is not compromised, req.user will stay only in your backend, which should be a trusted environment, and will not be sent back to your client via res by default.
Also, anything stored within req will only be available in the request itself, another request will have its own req instance, so data is not shared and should not leak to another request unless purposely made to do so.
However, you should always be staying on the ball, keep in mind to test and make sure all data sent back to your client does not have any sensitive info contained within them (e.g. password, tokens).
If you are not comfortable with storing that in req.user, you can always add a layer of middleware to strip the sensitive info before reaching your controller. This way, routes that use the middleware will not have sensitive info exposed.
I have created node js app using express framework.
I have created middleware for restricting access to some routes.
Middleware actually works fine. but i have difficulties in displaying data.
Suppose In My app i have created route for display list of countries('/country/master')i.e html page which is using internally different/default route ('/country/') to get data from mongoDB.
In this case user will not able to see data cause i have not given permission to "/" routes. but i want to display data but not allow him to make use of "/" route to check data.
How can i deal with this case ????
The answer depends on your authentication strategy i.e. are you using session identifiers, access tokens, etc.
In either case I suggest that you break out the credential exchange (aka login) from the authentication. They should be separate middleware functions. Below is an example of what this looks like.
While this answers your question, specific to ExpressJS, it does leave out a lot of other details that matter when you are building an authentication system (like how to securely store passwords). I work at Stormpath, we provide user management as an API so that you don't have to worry about all the security details! It's very easy to integrate our API into your application, using the express-stormpath module. You'll have a fully featured user database in minutes, without having to setup mongo or a user table.
All that said, here's the example:
/* pseudo example of building your own authentication middleware */
function usernamePasswordExchange(req,res,next){
var username = req.body.username;
var password = req.body.password;
callToAuthService(username,password,function(err,user){
if(err){
next(err); // bad password, user doesn’t exist, etc
}else{
/*
this part depends on your application. do you use
sessions or access tokens? you need to send the user
something that they can use for authentication on
subsequent requests
*/
res.end(/* send something */);
}
});
}
function authenticate(req,res,next){
/*
read the cookie, access token, etc.
verify that it is legit and then find
the user that it’s associated with
*/
validateRequestAndGetUser(req,function(err,user){
if(err){
next(err); // session expired, tampered, revoked
}else{
req.user = user;
next();
}
});
}
app.post('/login',usernamePasswordExchange);
app.get('/protected-resource',authenticate,function(req,res,next){
/*
If we are here we know the user is authenticated and we
can know who the user is by referencing req.user
*/
});
You can positioning of middleware in you app.for example:-
app.get('/country/master',function(req,res){
})
app.use(function(req,res){
your middle ware for providing authentication
})
// other routes where authentication should be enabled
app.get('other urls')
I am building an authentication system using Passport.js using Easy Node Authentication: Setup and Local tutorial.
I am confused about what passport.session() does.
After playing around with the different middleware I came to understand that express.session() is what sends a session ID over cookies to the client, but I'm confused about what passport.session() does and why it is required in addition to express.session().
Here is how I set up my application:
// Server.js configures the application and sets up the webserver
//importing our modules
var express = require('express');
var app = express();
var port = process.env.PORT || 8080;
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');
var configDB = require('./config/database.js');
//Configuration of Databse and App
mongoose.connect(configDB.url); //connect to our database
require('./config/passport')(passport); //pass passport for configuration
app.configure(function() {
//set up our express application
app.use(express.logger('dev')); //log every request to the console
app.use(express.cookieParser()); //read cookies (needed for auth)
app.use(express.bodyParser()); //get info from html forms
app.set('view engine', 'ejs'); //set up ejs for templating
//configuration for passport
app.use(express.session({ secret: 'olhosvermelhoseasenhaclassica', maxAge:null })); //session secret
app.use(passport.initialize());
app.use(passport.session()); //persistent login session
app.use(flash()); //use connect-flash for flash messages stored in session
});
//Set up routes
require('./app/routes.js')(app, passport);
//launch
app.listen(port);
console.log("Server listening on port" + port);
passport.session() acts as a middleware to alter the req object and change the 'user' value that is currently the session id (from the client cookie) into the true deserialized user object.
Whilst the other answers make some good points I thought that some more specific detail could be provided.
app.use(passport.session());
is equivalent to
app.use(passport.authenticate('session'));
Where 'session' refers to the following strategy that is bundled with passportJS.
Here's a link to the file:
https://github.com/jaredhanson/passport/blob/master/lib/strategies/session.js
And a permalink pointing to the following lines at the time of this writing:
var property = req._passport.instance._userProperty || 'user';
req[property] = user;
Where it essentially acts as a middleware and alters the value of the 'user' property in the req object to contain the deserialized identity of the user. To allow this to work correctly you must include serializeUser and deserializeUser functions in your custom code.
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (user, done) {
//If using Mongoose with MongoDB; if other you will need JS specific to that schema.
User.findById(user.id, function (err, user) {
done(err, user);
});
});
This will find the correct user from the database and pass it as a closure variable into the callback done(err,user); so the above code in the passport.session() can replace the 'user' value in the req object and pass on to the next middleware in the pile.
From the documentation
In a Connect or Express-based application, passport.initialize()
middleware is required to initialize Passport. If your application
uses persistent login sessions, passport.session() middleware must
also be used.
and
Sessions
In a typical web application, the credentials used to authenticate a
user will only be transmitted during the login request. If
authentication succeeds, a session will be established and maintained
via a cookie set in the user's browser.
Each subsequent request will not contain credentials, but rather the
unique cookie that identifies the session. In order to support login
sessions, Passport will serialize and deserialize user instances to
and from the session.
and
Note that enabling session support is entirely optional, though it is
recommended for most applications. If enabled, be sure to use
express.session() before passport.session() to ensure that the login
session is restored in the correct order.
While you will be using PassportJs for validating the user as part of your login URL, you still need some mechanism to store this user information in the session and retrieve it with every subsequent request (i.e. serialize/deserialize the user).
So in effect, you are authenticating the user with every request, even though this authentication needn't look up a database or oauth as in the login response. So passport will treat session authentication also as yet another authentication strategy.
And to use this strategy - which is named session, just use a simple shortcut - app.use(passport.session()). Also note that this particular strategy will want you to implement serialize and deserialize functions for obvious reasons.
It simply authenticates the session (which is populated by express.session()). It is equivalent to:
passport.authenticate('session');
as can be seen in the code here:
https://github.com/jaredhanson/passport/blob/42ff63c/lib/authenticator.js#L233
I was looking for a way to let my client authorize with the facebook JS SDK and then somehow transfer this authorization to my node server (so it can verify requests with the fb graph api)
I stumbled across:
https://github.com/jaredhanson/passport-facebook/issues/26
&
https://github.com/drudge/passport-facebook-token
what seems to be an entirely different strategy from passport-facebook.
Am I correct when assuming that:
One logs in with the fb JS SDK, and then the facebook-token strategy somehow extracts the token and fb id from the document or body object?
Or is there any other decent way to achieve this? I'm namely trying to avoid the redirects enforced by the server SDKs
I've spent a couple of days this week trying to figure out the best way to use Facebook Authentication for a private API, using passport.js — passport-facebook-token is perfect for this.
You are correct in assuming these are two separate authentication strategies. You don't need passport-facebook installed to use passport-facebook-token.
If you have Facebook authentication implemented in the client-side JS (or iOS etc.), and are looking for a way to then authenticate API requests using your user's Facebook authToken, passport-facebook-token is a really elegant solution.
passport-facebook-token works totally independently of passport-facebook, and basically handles the redirects required by Facebook internally, before passing the request along to your controller.
So to authenticate an API route using passport-facebook-token, you'll need to set up a passport strategy like so:
passport.use('facebook-token', new FacebookTokenStrategy({
clientID : "123-your-app-id",
clientSecret : "ssshhhhhhhhh"
},
function(accessToken, refreshToken, profile, done) {
// console.log(profile);
var user = {
'email': profile.emails[0].value,
'name' : profile.name.givenName + ' ' + profile.name.familyName,
'id' : profile.id,
'token': accessToken
}
// You can perform any necessary actions with your user at this point,
// e.g. internal verification against a users table,
// creating new user entries, etc.
return done(null, user); // the user object we just made gets passed to the route's controller as `req.user`
}
));
It's worth noting that the User.findOrCreate method used in the passport-facebook-token Readme is not a default mongo/mongoose method, but a plugin that you'll have to install if you want it.
To use this auth strategy as middleware for any of your routes you'll need to pass it an access_token object either as a URL parameter, or as a property of the request body.
app.get('/my/api/:access_token/endpoint',
passport.authenticate(['facebook-token','other-strategies']),
function (req, res) {
if (req.user){
//you're authenticated! return sensitive secret information here.
res.send(200, {'secrets':['array','of','top','secret','information']});
} else {
// not authenticated. go away.
res.send(401)
}
}
NB. the access_token property is case-sensitive and uses an underscore.
The documentation for passport-facebook-token isn't extensive, but the source is really well commented and pretty easy to read, so I'd encourage you to take a look under the hood there. It certainly helped me wrap my head around some of the more general ways that passport works.