Which passport authentication to use

Problem

I'm using several Passport authentication strategies in my site, which works great, however, I need a Demo or (we could call session ) Strategy which automatically authorize the user by it's session id, Now I'm doing it manually, when the user navigates to the /demo page, I run a query on the db (mongodb), with the session id, and if the user exists, I render the page with that user, if not I create one.

app.get('/demo', function(req,res) {
  db.User.findOne({ 'accounts.kind': 'demo', 'accounts.sid': req.sessionID }, function(err, user) {
    if (user) {
      res.render( 'home', {
        user: user
      });
    } else {
      var user = new db.User();
      user.accounts.push({
        kind: 'demo',
        sid: req.sessionID,
        created: Date.now
      });
      req.session.userId = user._id;

      user.save(function(err) {
        if(err) { throw err; }
        res.render( 'home', {
          user: user
        });
      });
    }
  });
});

the user schema looks like this:

UserSchema = new Schema({
  uname: {type: String},
  accounts: [],
  docs:[{type:Schema.Types.ObjectId, ref:'Doc'}]
})

It works, but I need to set a session variable, because this method does not log in the user, I mean there is no req.user in the requests, but with that session variable I can check whether the user is a demo user.

if (req.user) {
    userid = req.user._id.toString();
  } else {
    userid = req.session.userId;
  }

I'm sure there's a more elegant way of doing this, with one of the existing passport Strategies. I see that there's passport-http, and passport-anonymous, and several others, but I'm not sure which should I use.

It's important to me to create a database entry for the user. So later I can attach to it an another account.

Problem courtesy of: balazs

Solution

I could solve it like this: Defined a new Strategy,

var passport = require('passport')
  , util = require('util');

function DemoStrategy(options, verify) {
  if (typeof options == 'function') {
    verify = options;
    options = {};
  }
  if (!verify) throw new Error('Demo Basic authentication strategy requires a verify function');

  passport.Strategy.call(this);
  this.name = 'demo';
  this._verify = verify;
}

util.inherits(DemoStrategy, passport.Strategy);

DemoStrategy.prototype.authenticate = function(req) {
  var self = this;
  this._verify( req, function(err, user) {
    if (err) { return self.error(err); }
    self.success(user);
  });
}

module.exports.Strategy = DemoStrategy;

the route:

app.get('/auth/demo', 
    passport.authenticate('demo'),
    function(req,res) { 
      res.redirect('/');
     }
  );

and the passport.use part:

passport.use(new DemoStrategy( 
  function(req, done) {
    console.log( 'using demo strategy, req: ', req );
    if (req.user) {
      // place code here, if we want to attach to an existing account
    } else {
      db.User.findOne({ 'accounts.kind': 'demo', 'accounts.sid': req.sessionID }, function(err, dbuser) {
        if (dbuser) {
          done(null,dbuser);
        } else {
          var user = new db.User();
          user.accounts.push({
            kind: 'demo',
            sid: req.sessionID,
            created: Date.now
          });

          user.save(function(err) {
            if(err) { throw err; }
            done(null,user);
          });
        }
      });
    }
  }
));

Maybe it's full of bugs, and the error handling is not complete, but seems like the right solution, it's integrated into the much appreciated passport library,

Solution courtesy of: balazs

Discussion

Check out Section 4 from the passport configuration. This is the code to put the session-id into a cookie and retrieve the data when the user comes back. No strategy required.

passport.serializeUser(function(user, done) {
  done(null, user.id);
});

passport.deserializeUser(function(id, done) {
  User.findById(id, function (err, user) {
    done(err, user);
  });
});
Discussion courtesy of: Patrick

As it turns out, creating a custom strategy is unnecessary in this case. I found out that the official LocalStrategy has an (undocumented) passReqToCallback option, which enables you to access req object in the verify function. You can use it to do all kinds of stuff using the LocalStrategy only by altering the verify function:

passport.use(new LocalStrategy({
    passReqToCallback: true
}, function(req, username, password, done) {
    //now that you have req object, you can
    //probably do whatever you need to do right here
});

Please mind that the function now takes 4 arguments req, username, password, done, beginning with req object, instead of the default 3.

I hope this saves you some time, I spent like an hour trying to work around this before going through the LocalStrategy code.

Discussion courtesy of: phoenicks

This recipe can be found in it's original form on Stack Over Flow.