Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js Authentication library with "persistence token" functionality

I have a working knowledge of passport for node, but it doesn't have things such as:

  • generating a "persistence token" (such as in authlogic/session/session.rb#L35
  • generating perishable tokens for password resets
  • remember me functionality
  • managing those properties for login/logout on some model class, etc.

Are there any libraries that have solved this in the Node.js community? If there is anything as robust (or on it's way to being as robust) as Devise for Rails, that would be perfect, but anything that solves this token issue will work just as well.

The crazy thing is a lot of the examples out there are storing the user id in the session!

request.session['userId'] = user.get('id')

That is just asking to be hacked.

It should be something like this:

require.session['persistenceToken'] = App.User.generateRandomToken()
like image 995
Lance Avatar asked Jun 26 '12 01:06

Lance


2 Answers

A one-time password (aka single-use token) strategy for password resets is something that I'll be implementing. Given Passport's architecture, this can easily be a separate module and it wouldn't surprise me to find out someone else has already implemented such a thing.

Remember me and persistence token functionality is also something I'd like to support. Preferably, I'd like that to be a separate strategy as well, but it may need some core support. If that turns out to be the case, a few extra lines in req.logIn ( https://github.com/jaredhanson/passport/blob/master/lib/passport/http/request.js#L28 ) should be able to cover it.

As for storing a user ID in the session, I don't see any big risk given that, by default in Connect/Express, session properties are stored entirely in the backend and looked up by a unique sid that is set in an encrypted cookie. A malicious user would have to possess both the unique sid and session secret in order to spoof requests.

Mozilla is using Passport as part of their identity effort, to bridge BrowserID to other providers which lack BrowserID support (see browserid-bigtent). Given their requirements, developers can be sure that Passport meets strict security requirements.

(Ultimately session serialization in Passport is an application's responsibility, so a random token in place of a user ID can be used if desired for security reasons. Obviously this should be done if storing data directly in the cookie, but I'd suggest that doing that is the most ill-advised approach.)

As far as managing those properties on a model, Passport is designed to be completely model/ORM agnostic. I don't intend to ever change that fact, as I think those decisions are best left to the application (and Passport is more flexible as a result of delegating this responsiblity). That said, I think there is room for other modules to independently build on top of Passport to provide this functionality.

All that said, I think Passport is the most robust of the existing Node.js auth solutions. Your first three requests would go a long way to making it more so, and they should be easy to accomplish. I'd love to collaborate on getting these features in, so don't hesitate to get in contact with me.

Finally, in case anyone is curious, first-class API authentication is presently in the works, on the authinfo branch. Building on this, passport-http-oauth implements OAuth server strategies, which can be combined with oauthorize middlware as a toolkit to assemble OAuth servers. This isn't fully baked yet, but it'll be another effective feature of Passport when its ready.

like image 107
Jared Hanson Avatar answered Nov 15 '22 00:11

Jared Hanson


In the meanwhile, something like the following should be sufficient. Adjust as necessary to fit your app and desired cookie duration. Checking for username & password is a kind of cheesy way to detect a login attempt, but it works well enough with passport.

app.use(function(req, res, next) {
    if(
            typeof(req.body.username) !== "undefined"
            && typeof(req.body.password) !== "undefined"
            ) {
        if(req.body.remember == 1) {
            req.session.cookie.maxAge = 365*24*60*60*1000;
        }
        else {
            req.session.cookie.maxAge = 24*60*60*1000;
        }
    }
    next();
});
like image 33
Justen Robertson Avatar answered Nov 15 '22 00:11

Justen Robertson