Update 5/11/2016: Developing in Node.js? Check out some of our Greatest Hits:
- A 15-Minute App Tutorial for AngularJS + Node.js + Stormpath,
- Everything you ever wanted to know about sessions in Node but were afraid to ask, and
- A Simple Web App with Node.js, Express, Bootstrap, and Stormpath.
Also, don’t miss nJWT, the cleanest JSON Web Token (JWT) library developed by Stormpath for Node.js developers!
Node is blowing up! I’ve been working and playing with Node since 2010 and in that time I’ve seen it go from a tiny community of people hacking side projects to a full-fledged and legit movement of modern developers building very real, very important, and very large applications. A whole ecosystem of solutions has sprung up to help Node developers, and that ecosystem is rapidly evolving. But it’s increasingly hard to figure out what solutions are best for you because of all the noise in a Google search or in npm
.
User authentication and management, in particular, is a difficult and shifting landscape. And yet, when building a real application in Node, it is one of the first components you need to figure out. This guide aims to give you a complete lay of the land for user management and authentication in node.
What is available in Node?
Node today has several different paths to build user management. In no particular order there are:
- Passport.js / Everyauth
- Your Own Database and a Hashing Algorithm
- User Management as a Service
Passport.js / Everyauth
PassportJS and Everyauth are authentication middleware for node that leverage the Connect middleware conventions. That means if you are using a framework like Express, Restify, or Sails you can easily plug one of their authentication schemes (or strategies) directly into your application. Everyauth comes with their strategies embedded, where as with Passport you can pick and choose which strategies to use. Some of the common strategies that developers use with Passport are Facebook and Google, but it also include everything from a local username/password authentication to a slew of OpenID and OAuth providers, and even a Stormpath strategy for Passport.
Even though Everyauth and Passport are built on top of the same middleware framework, they have their own sets of pros and cons. Passport is more flexible and modular, but Everyauth provides additional functionality that helps with routes and login/registration views. For a lot of Node developers, Passport is also preferred because it does not use promises. Here is a great example app using Node.js, Passport, Express, and Stormpath.
Since Passport and Everyauth are built on Connect, both will help you with Node.js user session management , including:
- Serialization of the authenticated user
- Managing the session
- Logging the user out
Additionally, they are designed for simple, easy authentication, but fall short (by design) of broader user management needs. You are still left to design, implement, and maintain all your other user infrastructure.
For example, if you are using passport-local (the strategy to authenticate username / password against your own database), Passport does not handle user signup and account verification. You will need to work with database modules to authenticate to a database, create the account, track verification status, create a verification token, send an email, and verify the account. This means a developer will need to worry about URL safety, removing expired tokens, and other security constraints (like hashing the password correctly in a database).
Your Own Database and a Hashing Algorithm
The do-it-yourself approach doesn’t rely on any middleware. You choose your own stack, a database to store users (likely PostgresSQL and MongoDB) and a hashing algorithm to generate password hashes (likely bcrypt and scrypt). Searching for bcrypt or scrypt in npm will result in quite a few modules of varying degrees of quality, each with their own sets of dependencies. Be particularly careful if you are a Windows developer – we recommend the native JS implementation of bcrypt.
After deciding on a stack, you will need to build out user management and authentication. Historically this approach has been extremely common, but is tedious, prone to error, and requires more maintenance than other approaches.
You will need to build/figure out:
- Account Creation
- Create a user schema to hold user data
- Create accounts and store salt + hashed passwords using bcrypt / scrypt
- Sending an email with token for account verification
- Account Authentication
- Authenticate the user (comparing hashes)
- Account Management
- Password reset work flow
- Generating / invalidating tokens
- Role-based access / permissions
- Password reset work flow
- Integrations with ID and Social Providers
- Secure the system
- Secure the Database from unauthorized access
- Secure the OS from unauthorized access
- Backups for data
One of the biggest challenges with rolling your own auth and user management is the maintenance. Take password hashing as an example. Done right, you select a cost factor that makes your hashing algorithm purposely slow (roughly 300-700ms) to prevent brute force attacks. However, Moore’s Law is a bitch— compute price/performance doubles every year. So the right cost factor today may be considered insecure tomorrow. If you’re building applications that will go into production, its your responsibility to update your hashing strategy at least once a year.
Despite the Node community’s aversion to “rolling your own” middleware, there are a few benefits to this approach. You get complete control of your infrastructure. If the tools available to you are not “good enough” for what you need to deliver to your customer, then you have the ability to invest engineering effort and time to innovate around user authentication and user management. Luckily, very few applications and developers have those kind of requirements, so the open source tools and API services available today help them move faster and deliver more.
User Management as a Service
Over time, software has moved from on-premise, to the cloud, to distributed API services. At the same time, development teams have come to rely on open source software and API services as much as their own code. It is now possible to offload user management to a system that exposes user management functionality via REST APIs and open-source SDKs for application development. Stormpath falls into this category along with some user API startups. The Node community, in particular, has adopted this service-oriented paradigm more than any other community to date.
Typically, API-driven services allow for more common functionality around user management, going beyond just authentication. The additional functionality varies by provider, but usually includes:
- Account email verification
- Password reset work flows
- Role-based access / permissions
- Schema-less User Profiles
- Two-factor authentication
- Single sign on between applications
- Social login integration (Facebook, Google, etc.)
- Integration to Node auth middleware like Passport
In addition to pure features, they also off-load much of the security and operations. They host all the infrastructure for you, scale to absorb your peak traffic, and handle the on-going security of your user data.
Offloading to an API service generally delivers convenience and improved security, while reducing development and maintenance costs. Developers now get to focus on building unique parts of their application.
However, there are trade-offs with using API services. You are introducing a 3rd party dependency to your application. It needs to be highly available, fast, portable, provide security during transport, and be flexible enough to meet your user data model.
Availability and performance are extremely important because a critical system like user authentication and management can not be offline or slow. Caching in their SDKS and being based on modern cloud infrastructures is a good start, but it is important that the service is prepared for the worst case scenarios – like whole data centers going down. And if you build on top of a service for user management, make sure you can subscribe to notifications of any outages.
Data portability is also critical – can you move user data in and out in safe (and easy) ways. While its easy to write a script to port unencrypted data over JSON, the ease of porting passwords will heavily depend on how you have stored any pre-existing data. For example, bcrypt is very portable by design, as it follows Modular Crypt Format (MCF). It is possible for multiple systems and languages to understand how to construct the hash by looking at the hash value itself. If you’re building a prototype and don’t use a service like Stormpath, we recommend starting with an MCF hash like bcrypt – it will be much easier to upgrade in the future.
Transport security between your application and the API Service is important in this approach compared to the approaches above. The additional network communication needs to be secured. For example, Stormpath supports only HTTPS and uses a custom digest auth algorithm to guard against replay and man in the middle attacks. You can read more about our security here.
The data model used by authentication services can vary widely – Salesforce, for instance, looks very different from the Stormpath data model, which is based on directories and therefore generic and flexibly. It’s worth digging in to make sure that the data model you have planned for your application, is supported by the service. This is particularly true for multi-tenant applications or SaaS.
Additionally, documentation of APIs varies widely – you should make sure your solution outlines what you need before you dive in.
Building User Management…
…is hard. Inside node, there are different solutions, each with a set of pros and cons. I hope this post helps you get the lay of the land. If you have any questions, suggestions or experiences that you want to share, feel free to leave a comment or reach out to me on Twitter @omgitstom. And if you’re interested in a User Management API service, check out Stormpath and our Official Node.js Support that also includes Express.js and Passport.js support.