Zlatko Zlatko - 6 months ago 54
Node.js Question

Passport.js optional authentication

Is there an optional authentication middleware from Passport.js?

Let's say I have a route,

/api/users
. I want to give just a list of users to the public, but to authenticated people, I want to add more fields.

Currently I have just a dumb custom method that does the same thing, but I wonder if:


  • Passport.js already provides such thing or

  • how can I make this a part of passport, like a plugin or so.



My method, roughly, looks like

function optionalAuth(req, res, next) {

var authHeader = req.headers.authorization;
var token = parseToken(authHeader); // just getting the OAuth token here
if(!token) {

return next();
}
User.findOne({
token: token
}, function(err, user) {

if(err) {
return res.json(401, {message: 'auth expired'});
};
if(user) {
req.user = user;
}
next();
});
}


This, however, seems dumb to me, and also not in passport-auth-strategies.js or some other auth layer where I think it should be. What is the better way to do it?

Bonus points for telling me if I'm doing the proper thing returning 401 if I find a token but it's invalid :)

Answer

Here's a simple PoC:

var express       = require('express');
var app           = express();
var server        = app.listen(3012);
var passport      = require('passport');
var LocalStrategy = require('passport-local').Strategy;

app.use(passport.initialize());

passport.use(new LocalStrategy(function(username, password, done) {
  if (username === 'foo' && password === 'bar') {
    return done(null, { username : 'foo' });
  }
  return done(null, false);
}));

app.get('/api', function(req, res, next) {
  passport.authenticate('local', function(err, user, info) {
    var data = { hello : 'world' };
    // Only if the user authenticated properly do we include secret data.
    if (user) {
      data.secret = '3133753CR37';
    }
    return res.send(data);
  })(req, res, next);
});

It's calling passport.authenticate 'manually' in the /api endpoint. That way, you get more control over how to deal with authentication error (which—in your situation—shouldn't be treated as errors but as a way of limiting the output).

Here's the output without proper authentication:

$ curl 'localhost:3012/api?username=foo&password=wrong'
{"hello":"world"}

And here's with:

$ curl 'localhost:3012/api?username=foo&password=bar'
{"hello":"world","secret":"3133753CR37"}

To use as a middleware:

var middleware = function(req, res, next) {
  passport.authenticate('local', function(err, user, info) {
    req.authenticated = !! user;
    next();
  })(req, res, next);
};

app.get('/api', middleware, function(req, res, next) {
  var data = { hello : 'world' };
  if (req.authenticated) {
    data.secret = '3133753CR37';
  }
  return res.send(data);
});