Introduction
User authentication is one of those things that you probably don't think too much about, but just about every website or app out there requires it. If you had to implement authentication yourself, could you? Well don't worry, you probably won't have to. Since this functionality is so common, just about every language/web framework I've come across already has a solution ready to go, and for Node, you can use the Express Passport plugin.
As always, I'd recommend you trust the popular packages for things like this as they've probably already gone through the headaches of finding bugs and vulnerabilities within the code.
What is Passport
The Passport package is an expandable and modular authentication middleware for Node.js that adds authentication functionality to your Express app. When most people think of authentication, they think of the traditional username and password combination. While this still is very popular, using other services to authenticate a user through OAuth has become another popular method. With Passport being a very extensible middleware, it allows you to plug in over 300 different authentication providers like Facebook, Twitter, Google, and more, most of which use the OAuth standard.
Not all of the strategies use OAuth, however. You can also choose from hash-based (passing a hash via query string, POST data, or in the HTTP headers), JSON web tokens, or even just HTTP Basic authentication (like this: http://username:[email protected]/
). Although some of these strategies may take different parameters, they will largely be the same so you should be able to easily swap strategies if needed.
To track sessions for a logged-in user between requests after the initial authentication, Passport saves session data in a cookie, which it creates and sends to the user. This is all handled behind the scenes for you for all strategies.
How to use Passport
To use Passport, you'll need to hook it up to your Express app
just like you would any other middleware:
var express = require('express');
var passport = require('passport');
var app = express();
app.configure(function() {
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.session({ secret: 'derpy' }));
app.use(passport.initialize());
app.use(passport.session()); // Required for persistent login sessions (optional, but recommended)
});
Take note that if you decide to enable sessions, then you need to use express.session()
before passport.session()
to ensure that the user's login session is restored in the right order.
In most cases, you'll want to enable sessions, otherwise the user won't be remembered between requests.
Once you have Passport initialized and the sessions set up, it's time to set up your strategy. Like I said earlier, most are pretty similar setup-wise, so let's just take a look at the most commonly used strategy, LocalStrategy
:
var LocalStrategy = require('passport-local').Strategy;
// Express/Passport setup here...
passport.use(new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password'
},
function(email, password, done) {
User.loadOne({ email: email }).then(function(user) {
if (!user || !user.authenticate(password)) {
return done(null, false, { message: 'Incorrect email or password.' });
}
done(null, user);
});
})
);
The User.loadOne...
part will be specific to your application (I'm using the Camo ODM here). The callback sends you the user's username/email and password, which in the case of "Local Strategy '' was retrieved from the submitted form data with input fields named email
and password
.
The HTML form that this data comes from might look something like this:
<form method="post" role="form">
<input type="text" name="email"/>
<input type="password" name="password"/>
<button type="submit">Login</button>
</form>
Notice the fields are named email
and password
like we specified in the LocalStrategy
above.
With this email and password being extracted from req
by Passport, we can look up the user in our database and then check the given password against the one we got from the database (don't worry, it's a salted hash of the password). Returning a valid user object to the done
callback tells Passport that the credentials were valid. Otherwise we return false
and an error message.
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
The last thing you need to do is tell Passport how to serialize and deserialize the user object. What this means is that Passport needs you to tell it how to uniquely identify a user with just a string, like with an ID or an email address.
// Express/Passport setup here...
// Passport strategy setup here...
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.loadOne({ _id: id }).then(function(user) {
done(null, user);
}).catch(function(err) {
done(err, null);
});
});
In the serializeUser
function above we have already retrieved the user data from the database and need to tell Passport what information to use in the cookie to uniquely identify the user, which in this case is user.id
.
For deserializeUser
, Passport got the cookie string (the user ID) and needs to turn it in to a user object. So we use the id
to look up the user in the database. What you return here is passed in each request as req.user
to the route.
Conclusion
Most web-apps and many native apps use some form of authentication, so you'd be pretty well off letting Passport handle all of the hard work for you. All you need to do is decide on a strategy.
Not that you need any more motivation to use Passport, but keep in mind that getting authentication right is hard, and with it being the gatekeeper to your users' sensitive data, you'd be better off trusting it to a package that's been thoroughly tested and proven.
What do you think of Passport, and how have you used it in the past? Let us know in the comments!