27

I'm using node.js with Express and connect-auth to authenticate users.

This is the verification when requesting /index:

if(req.isAuthenticated()) {
  res.redirect('/dashboard');
} else {
  res.render('index', { layout: 'nonav' });
}

However, after logging out and going back to f.e. '/dashboard', I can see the dashboard.

How can I put the authentication check to every request to make sure there's a valid user at all times?

I don't have any problems with the authentication, everything works fine! I need a solution which checks every route/request if there's a valid user, without putting a function or if-statement in the route-implementation, as the whole App needs a valid user anyway. The Express-Authentication-Example uses "restrict" in the route-definition, which is close, but with many routes it can easily be forgotten.

Chenmunka
  • 685
  • 4
  • 21
  • 25
Patrick
  • 7,903
  • 11
  • 52
  • 87
  • 2
    Have you gone through http://stackoverflow.com/questions/3498005/user-authentication-libraries-for-node-js and http://stackoverflow.com/questions/7814794/how-to-structure-a-node-express-connect-auth-and-backbone-application-on-the-s – Samyak Bhuta Oct 31 '11 at 12:16

3 Answers3

37
app.all('*',function(req,res,next){
    if(req.isAuthenticated()){
        next();
    }else{
        next(new Error(401)); // 401 Not Authorized
    }
});
// NOTE: depending on your version of express,
// you may need to use app.error here, rather
// than app.use.
app.use(function(err,req,res,next){
    // Just basic, should be filled out to next()
    // or respond on all possible code paths
    if(err instanceof Error){
        if(err.message === '401'){
            res.render('error401');
        }
    }
});

If you define the all route before routes which require authentication and after routes which do not (such as the home page, login, etc) then it should only affect the routes that need it. Alternatively you could use a RegExp instead of '*', which would include a subpath or list of paths that require authentication.

Another option would be to create a function to include in each route that requires auth:

function IsAuthenticated(req,res,next){
    if(req.isAuthenticated()){
        next();
    }else{
        next(new Error(401));
    }
}
app.get('/login',function(req,res,next){
    res.render('login');
});
app.get('/dashboard',IsAuthenticated,function(req,res,next){
    res.render('dashboard');
});
app.get('/settings',IsAuthenticated,function(req,res,next){
    res.render('settings');
});
Zikes
  • 5,888
  • 1
  • 30
  • 44
  • 2
    I like the `app.all` option. But it triggers on every request... even requests for public files! like .js and .css Can I avoid that? – Farzher Nov 08 '12 at 22:52
  • 1
    @StephenSarcsamKamenar Right now many people use an http server like nginx as a reverse proxy to node.js applications. I would recommend using that to route requests to static files whenever possible. – Zikes Nov 28 '12 at 19:26
  • 1
    I am pretty sure you can just do app.all("/api/*"...) for example, to only include it for certain paths. http://expressjs.com/api.html#app.all – Timmerz Mar 28 '13 at 22:02
  • Working with Restify, req.isAuthenticated() is actually a function just FYI for the curious – netpoetica May 23 '13 at 02:03
  • @Elisabeth Unfortunately I haven't worked with node or express in a couple of years, so it's gotten away from me. `return next()` may indeed be the new standard convention for this, but I will have to leave it to a developer that is more familiar with recent versions than I am to update the code in my answer if it is required. You are welcome to do so. – Zikes Nov 08 '13 at 19:41
5

You can use sessions mechanism provided by connect. Put this code in app.configure() to enable it:

  app.use(express.cookieParser());
  app.use(express.session({
    secret: 'some string used for calculating hash'
  }));

After that, you′ll be able to use req.session object (different for each request) to store your authentication data (or anything else). So, your example code will look something like this:

if (req.session && req.session.authorized) {
  res.redirect('/dashboard');
}
else {
  res.render('index', {layout: 'nonav'});
}

And authentication will look like this:

req.session.authorized = checkPassword(login, passw);

Logout:

req.session.destroy();

More info can be found here.

Aleksei Zabrodskii
  • 2,220
  • 3
  • 19
  • 41
  • Authentication and authorization works fine. I've updated my question. But thanks for the example! – Patrick Oct 31 '11 at 14:55
  • Ok, I get it now. Maybe [this](http://stackoverflow.com/questions/7053741/how-to-optimize-an-express-js-route/7054958#7054958) could help. – Aleksei Zabrodskii Oct 31 '11 at 15:19
  • Hm, the same as Ganesh's link. I was looking for something like Rails' before_filter on application level, but I guess node doesn't have that. – Patrick Nov 01 '11 at 10:01
0

Another way is to app.use a middleware function. (Example in CoffeeScript.)

# middleware
authKick = (req, res, next) ->
  if not do req.isAuthenticated then return res.redirect '/login'
  return do next

# apply
app.use authKick

This will work on each request without having to touch the routes.

Nelo Mitranim
  • 823
  • 1
  • 13
  • 13