Two-Factor Authentication with Node.js

There are a variety of strategies for protecting your important online credentials. We often hear about password managers and generators, but for me, the more important strategy is using two-factor authentication (2FA). Passwords can be guessed, phone numbers can be spoofed, but using two-factor authentication essentially requires that user be in possession of a physical device with an app like Google Authenticator, loaded with a secret key for the given app, which provides an extra layer of security.

I didn't use to take two-factor authentication seriously, until someone stole my domain name and tried to launder it to a safe haven for thieved domains. While I don't know how exactly they did it, I'm fairly certain they got access to my email address, created filters so I wouldn't see the emails, etc. Had I used two-factor authentication, neither my email or GoDaddy accounts could have been accessed. Or you could take it from Cody Brown who had $8,000 in cryptocurrency stolen in minutes because the vendor used phone number validation to allow transactions to be approved. Today I use two-factor authentication for all of my important email, work, and financial accounts.

Since I use 2FA so often, I wanted to see how the process is managed by a developer for its users. That would include generating the secret key, creating its QR code representation, scanning the code into Google Authenticator (done by the user), and then validating that GA-given code against the user's key. I found an easy to use Node.js library, speakeasy, to do so!

QRCode.toDataURL provides an image data URI that you can use for the imgsrc attribute. If you aren't familiar with a QR code, it will look something like this:

User Step 1: Scan the QR Code / Add Site to Authenticator

At this point the user should have opened Google Authenticator (or Authy, etc.) and scanned the QR code; an entry for your web app will be added within the device's app. From this point forward, whenever the user wants to log in (or perform any action you'd like to be protected), your system should recognize the user wants to use 2FA and you should require they enter the token from their app.

For the purposes of debugging, you can get what should be the user code value at a given time via:

User Step 2: Providing the Token / Validating the Token

When your web app prompts the user for the current 2FA token, and the user provides a 6 digit token, the web app must validate that token:

// This is provided the by the user via form POST
var userToken = params.get('token');
// Load the secret.base32 from their user record in database
var secret = ...
// Verify that the user token matches what it should at this moment
var verified = speakeasy.totp.verify({
secret: secret,
encoding: 'base32',
token: userToken
});

If the token matches, the user can be trusted; if the token does not match, the web app should prompt the user to try again. Remember that Authenticator provides a new token every {x} seconds so an incorrect token shouldn't immediately raise a red flag; the token may have simply expired by the time the user submitted the form.

Live Demo

The speakeasy developers have created a live speakeasy 2FA demo for you to play with so that you can understand the steps involved from both a user and a developer perspective.

This post is only meant to be a brief, high level overview of implementing two-factor authentication -- please read the speakeasy documentation to get a more detailed explanation as well as learn about more specific 2FA options. In an ideal world, two-factor authentication would be enabled by default for most logins, however it can be confusing for the majority of web users (think of the very non-technical user), so I can understand why 2FA is considered an extra security measure for now. A big thanks to speakeasy's developers for their easy to use Node.js library, awesome documentation, and simple demo!

When you say or read "HTML5", you half expect exotic dancers and unicorns to walk into the room to the tune of "I'm Sexy and I Know It." Can you blame us though? We watched the fundamental APIs stagnate for so long that a basic feature...

My team mate Edna Piranha is not only an awesome hacker; she's also a fantastic philosopher! Communication and online interactions is a subject that has kept her mind busy for a long time, and it has also resulted in a bunch of interesting experimental projects...

Everyone loves the MooTools Accordion plugin but I get a lot of requests from readers asking me how to make each accordion item open when the user hovers over the item instead of making the user click. You have two options: hack the original plugin...

Discussion

Another easy way to implement 2-Factor Authentication is to hand off the authentication work to something like AWS Cognito. Not only can it handle 2FA, but it can take care of all your authentication needs, freeing you up to focus on other things.

Oleks

Thanks for the nice article! I created some simple docker service for my project based on this info for my projects, somebody could find useful (no qr code scanning though): https://github.com/oleksm/2fa

Continue this conversation via emailGet only replies to your comment, the best of the rest, as well as a daily recap of all comments on this post. No more than a few emails daily, which you can reply to/unsubscribe from directly from your inbox.