tl;dr: I went on a search of Node.js/Express.js authentication tutorials. All of them were incomplete or made a security mistake in some way that can potentially hurt new users. This post explores some common authentication pitfalls, how to avoid them, and what to do to help yourself when your tutorials don’t help you anymore. I am still searching for a robust, all-in-one solution for authentication in Node/Express that rivals Rails’s



Don't forget to

Feel free to check out our hotel price comparison api which at



I went on a search of Node.js/Express.js authentication tutorials. All of them were incomplete or made a security mistake in some way that can potentially hurt new users. This post explores some common authentication pitfalls, how to avoid them, and what to do to help yourself when your tutorials don’t help you anymore. I am still searching for a robust, all-in-one solution for authentication in Node/Express that rivals Rails’s Devise Don't forget to Retweet the article.Feel free to check out our hotel price comparison api which at makcorps.com.

In my spare time, I’ve been digging through various Node.js tutorials, as it seems that every Node.js developer with a blog has released their own tutorial on how to do things the right way, or, more accurately, the way they do them. Thousands of front-end developers being thrown into the server-side JS maelstrom are trying to piece together actionable knowledge from these tutorials, either by cargo-cult-copypasta or gratuitous use of npm install as they scramble frantically to meet the deadlines set for them by outsourcing managers or ad agency creative directors.

. The de facto authentication solution in the Express.js world is strategies for authentication. If you want a robust solution similar to One of the more questionable things in Node.js development is that authentication is largely left as an exercise to the individual developerTheauthentication solution in the Express.js world is Passport , which offers a host offor authentication. If you want a robust solution similar to Plataformatec’s Devise for Ruby on Rails, you’ll likely be pointed to Auth0 , a startup who has made authentication as a service.

Compared to Devise, Passport is simply authentication middleware, and does not handle any of the other parts of authentication for you: that means the Node.js developer is likely to roll their own API token mechanisms, password reset token mechanisms, user authentication routes and endpoints, and views in whatever templating language is the rage today. Because of this, there are a lot of tutorials that specialize in setting up Passport for your Express.js application, and nearly all of them are wrong in some way or another, and none properly implement the full stack necessary for a working web application.

Note: I’m not attempting to harass the developers of these tutorials specifically, but rather I am using their authentication mistakes to show security issues inherent in rolling your own authentication systems. If you are a tutorial writer, feel free to reach out to me once you’ve updated your tutorial. Let’s make Node/Express a safer ecosystem for new developers.

Mistake one: credential storage

Let’s start with credential storage. Storing and recalling credentials is pretty standard fare for identity management, and the traditional way to do this is in your own database or application. Passport, being middleware that simply says “this user is cool” or “this user is not cool”, requires the passport-local module for handling password storage in your own database, written by the same developer as Passport.js itself.

Note for experienced developers: If you really want to work with state of the art credential storage, Argon2 is the winner of the Password Hashing Competition and now has some easy support in Node.js. Unfortunately, documentation for the implementation of Argon2 for novice users in the Node.js ecosystem is still lacking, and given that this is targeted toward people reading tutorials, sticking with bcrypt for the next few months seemed an OK tradeoff.

passport-local itself, which As a new Express.js and Passport user, my first place to look will be the example code foritself, which thankfully gives me a sample Express.js 4.0 application I can clone and extend. However, if I just copypasta this, I’m not left with too much, as there’s no database support in the example and it assumes I’m just using some set accounts.

It’s just an Intranet application, the dev says, and I have four other projects assigned to me due next week. Of course, the passwords for the example aren’t hashed in any way, That’s OK, though, right?, the dev says,. Of course, the passwords for the example aren’t hashed in any way, and stored in plaintext right alongside the validation logic in this example . Credential storage isn’t even considered in this one.

Password storage aside, neither of these tutorials implement password reset functionality, which is left as an exercise to the developer and comes with its own pitfalls.

Mistake two: password reset

A sister security issue to password storage is that of password reset, and none of the top basic tutorials explain how to do this at all with Passport. You’ll have to follow another.

There are a thousand ways to fuck this up. The most common ways I have witnessed that people get password reset wrong are:

Predictable tokens. Tokens that are based upon the current time are a good example. Tokens made by bad pseudorandom number generators are less obvious. Bad storage. Storing unencrypted password reset tokens in your DB means that if the DB is compromised, those tokens are effectively plaintext passwords. Generating a long token with a cryptographically secure random number generator stops remote brute force attacks on reset tokens, but it doesn’t stop local attacks. Reset tokens are credentials and should be treated as such. No token expiry. Not expiring your tokens gives attackers more time to exploit the reset window. No secondary data verification. Security questions are the de facto data verification for a reset. Of course, then the developer has to choose good security questions. Security questions have their own problems. While this may seem like security overkill, the email address is something you have, not something you know, and conflates the authentication factors. Your email address becomes the key to every account that just sends a reset token to email.

If you’re new all of this, try OWASP’s Password Reset Cheat Sheet . Let’s get back to what the Node world has to offer for us on this.

express passport password reset. Here is our old friend bcrypt again, with an even smaller cost factor of 5 used in the text, which is far too small of a cost factor for modern use. Alright, back to Google, for the only tutorial that seems to exist out there. We’ll take the first result for the Google searchHere is our old friendagain, with an even smaller cost factor of 5 used in the text, which istoo small of a cost factor for modern use.

However, this tutorial is pretty solid compared to others in that it uses crypto.randomBytes to generate truly random tokens, and expires them if they aren’t used. However #2 and #4 of the practices above aren’t honored by this comprehensive tutorial, and thus the password tokens themselves are vulnerable to authentication mistake number one, credential storage.

Thankfully, this is of limited use thanks to the reset expiry. However, these tokens are especially fun if an attacker has read access to the user objects in the DB via BSON injection or can access Mongo freely due to misconfiguration. The attacker can just issue password resets for every user, read the unencrypted tokens from the DB, and set their own passwords for user accounts instead of having to go through the costly process of dictionary attacks on bcrypt hashes with a GPU rig.

Mistake three: API tokens

API tokens are credentials. They are just as sensitive as passwords or reset tokens. Most every developer knows this and tries to hold their AWS keys, Twitter secrets, etc. close to their chest, however this doesn’t seem to transfer into the code being authored.

passport-jwt and decided to implement the JWT strategy. In any case, JWT is where everyone seems to be moving in the Node.js sphere of influence. (The venerable Let’s use JSON Web Tokens for API credentials. Having a stateless, blacklistable, claimable token is better than the old API key/secret pattern that has been used for the better part of a decade. Perhaps our junior Node.js dev has heard of JWT somewhere before, or sawand decided to implement the JWT strategy. In any case, JWT is where everyone seems to be moving in the Node.js sphere of influence. (The venerable Thomas Ptacek will argue that JWT is bad but I’m afraid that ship has sailed here.)

express js jwt on Google, and then find which is the first tutorial result. Unfortunately, this doesn’t actually help us at all, since it doesn’t use Passport, but while we’re here we’ll quickly note the mistakes in credential storage: We’ll search foron Google, and then find Soni Pandey ’s tutorial User Authentication using JWT (JSON Web Token) in Node.js which is the first tutorial result. Unfortunately, this doesn’t actually help us at all, since it doesn’t use Passport, but while we’re here we’ll quickly note the mistakes in credential storage:

We’ll store the JWT key in plaintext in the repository. We’ll use a symmetric cipher to store passwords. This means that I can get the encryption key and decrypt all of the passwords in event of a breach. The encryption key is shared with the JWT secret. We’ll use AES-256-CTR for password storage. We shouldn’t be using AES to start, and this mode of operation doesn’t help. I am not sure why this mode specifically was chosen, but the choice alone leaves the ciphertext malleable.

Welp. Let’s back out to Google and we find the next tutorial. Scotch, which did an OK job with password storage in their passport-local tutorial, just ignores what they told you before and stores the passwords in plaintext for this example.

Uh, we’ll give that a pass for brevity, but it doesn’t help the copypasta crew. That’s because more interestingly, this tutorial also serializes the mongoose User object into the JWT

DeprecationWarning or three from Mongoose, we can hit to create the user, then get a token by posting to /api/authenticate with the default credentials of “Nick Cerminara” and “password”. A token is returned, as displayed from Postman. Let’s clone the Scotch tutorial repository, follow the instructions, and run it. After aor three from Mongoose, we can hit http://localhost:8080/setup to create the user, then get a token by posting to /api/authenticate with the default credentials of “Nick Cerminara” and “password”. A token is returned, as displayed from Postman.

A JWT token returned from the Scotch tutorial.

Note that JSON Web Tokens are signed, but not encrypted. That means that big blob between the two periods is a Base64-encoded object. Quickly decoding it, we get something interesting.

I love my passwords in plaintext in tokens.

Now, anyone that has even an expired token has your password, as well as whatever else is stored in the Mongoose model. Given that this one came over HTTP, I could have sniffed it off of the wire.

Mistake four: rate limiting

As I alluded to above, I did not find a mention of rate limiting or account locking in any of these authentication tutorials.

Without rate limiting, an adversary can perform online dictionary attacks in which a tool like Burp Intruder is run in hopes of gaining access to an account with a weak password. Account lockout also helps with this problem by requiring extended login information from a user the next time they log in.

Remember, rate limiting also helps availability. bcrypt is a CPU-intensive function, and without rate limiting functions using bcrypt become an application-level denial of service vector, especially at high work factors. Multiple requests for user registration or login password checking are an easy way to turn a lightweight HTTP request into costly time for your server.

Authentication is hard.

I’m sure the tutorial developers will defend themselves with “This is just meant to explain the basics! Surely nobody will do this in production!” However, I cannot emphasize enough just how false this is. This is especially true when code is put out there in your tutorials. People will take your word for it — after all, you do have more expertise than they do.

If you’re a beginner, don’t trust your tutorials. Copypasta from tutorials will likely get you, your company, and your clients in authentication trouble in the Node.js world. If you really need strong, production-ready, all-in-one authentication libraries, go back to something that holds your hand better, has better stability, and is more proven, like Rails/Devise.

The Node.js ecosystem, while accessible, still has a lot of sharp edges for JavaScript-based developers needing to write production web applications in a hurry. If you have a front-end background and don’t know other programming languages, I personally believe it is easier to pick up Ruby and stand on the shoulders of giants than it is to quickly learn how not to shoot yourself in the foot when writing these types of things from scratch.

If you’re a tutorial writer, please update your tutorials, especially the boilerplate code. This code will become copypasta into others’ production web applications.

If you are a die-hard Node.js developer, hopefully you’ve learned a few things not to do in your authentication system you’re rolling with Passport. You will likely get something wrong. I haven’t gotten close to covering all of the ways to get it wrong in this one post. It shouldn’t be your job to roll your own auth for your Express application. There should be something better.

@koolwal_manthan If you’re interested in better securing the Node ecosystem, please DM meon Twitter.

This post was brought to you by espresso because I’m out of sake.

Addendum (last updated August 10, 2017)

This post started out of frustration from the problems I had been seeing, and has somehow taken on a life of its own. It has received more responses, more feedback, and caused more controversy than I had expected it to. Clearly this has been a point of frustration for other developers.

If you find errors in this post, please message me and we’ll work to fix them. What we don’t need is another misguided post. That’s the opposite of what we need.

However, I implore you: let’s not just work on this post. Let’s make the ecosystem better in the future. If you find errors or have other practices, things you’ve learned in production, or simply improvements, feel free to DM me. It seems that the next best step for us is to write a living document containing “cheat sheet” style best practices around authentication with Node/Express as a patch until there are proven, robust solutions.



