TypeScript Express tutorial #11. Node.js Two-Factor Authentication

Express JavaScript Node.js

This entry is part 11 of 15 in the TypeScript Express tutorial

Identity theft is a serious issue nowadays. With so many accidents happening on the web, it is a great time to learn about providing an additional layer of security for our users. In this article, we go through the principles and implementation of Node.js Two-Factor Authentication (2FA). We do that by using Google Authenticator and a Node.js server.

The source code is available in the GitHub repository in the part-11 branch. Feel free to give it a star.

Implementing Node.js Two-Factor Authentication

With Two-Factor Authentication, the user needs to prove his identity in two ways. A straightforward example of that is using an ATM. You need a bank card – something you possess – and the PIN code – something you know. Another example is combining your regular password with a one-time code that your smartphone generates.

Generating a secret key

First, we need to create a secret key, unique for every user.

For effortless Node.js Two-Factor Authentication handling, we use the speakeasy library. Its first job is to generate a secret key for us.

authentication.service.ts

Here we add a new environment variable – it determines the name that will be visible in the Google Authenticator application.

There are two essential things that our   returns. One of them is the secret code in the base32 format. We use it to validate the identity of the user later.

The second thing is  . It is a One Time Password Authentication (OTPA) compatible with Google Authenticator. We can use it to generate a Quick Response (QR) code that we display for the user.

Creating a QR image

Applications like the Google Authenticator allow users to add a page that they authenticate to either by manually entering a key, or scanning a QR code. The latter is way faster and is a standard right now. To generate QR images, we use a library called qrcode.

The most suitable function for us that it has is called  . It writes the QR code to a writable stream. An example of such is the response object!

If you want to dive deeper, check out Writable streams, pipes, and the process streams

authentication.service.ts

Once we have all of the above, we can create our new endpoint in the authentication controller.

authentication.controller.ts

As you can see, we also save the generated code in the database. It later comes in handy when turning on Two-Factor Authentication. Please note that a user needs to be logged in for it to work.

Node.js Two-Factor Authentication Postman

Since we have the generated code, we can use the Google Authenticator now:

Node.js Two-Factor Authentication Google Authenticator

We now have a fully functional workflow of generating a secret code and presenting it to the user!

Turning on Node.js Two-Factor Authentication

Currently, we only generate secret codes, but we haven’t yet turned on the Node.js Two-Factor Authentication for a user. For it to happen, we need a separate endpoint that the user sends his first verification code to. To begin, we need a function that validates the upcoming verification code.

authentication.service.ts

The   method verifies our Time-based One-time Password (TOTP) that user got from the Google Authenticator app against the secret code that we generated and saved in the database previously. Once we got that, we can create an endpoint that turns on the Two-Factor Authentication.

authentication.controller.ts

If you want to know how the validationMiddleware works, check out Error handling and validating incoming data

In the   function, we check if the provided code is valid. If that’s the case, we enable the Two-Factor Authentication by setting the  flag to true.

In this part of the series, we’ve made some minor change to the User model. You can look it up in the repository.

Logging in using our Node.js Two-Factor Authentication

The last part is logging in and authenticating using the Node.js Two-Factor Authentication. To implement it, we use the   function again. To make it more clear, let’s review the whole flow of authentication:

  1. The user attempts to log in using his email and a valid password, and we give him a JWT token.
    • If he doesn’t have the 2FA turned on, this gives him full access.
    • If he does have the 2FA turned on, we provide him with access just to the   endpoint.
  2.  The user sends a valid code to the   endpoint and is given a new JWT token with full access

That being said, let’s create a new route that allows the user to authenticate using a JWT token.

authentication.controller.ts

In the code above, we validate the upcoming  . If it is valid, we create and send back a new token. We respond with the user details, excluding the password and the Two-Factor Authentication code. To do this, we modify the   function:

authentication.service.ts

Now the JWT token also carries the information about the Two-Factor Authentication. The only thing left to do is to alter the authMiddleware.

The authMiddleware

We need the authMiddleware to check the  flag and throw an error if it is set to false when the user has 2FA turned on. Also, since there is one endpoint that should work even with the 2FA turned off, we need an option to omit it.

auth.middleware.ts

For a step-by-step explanation of authentication with the email and the password, check out Registering users and authenticating with JWT

In the code above, we demand the   to be true, if the user has the Two-Factor Authentication enabled and the  flag isn’t set to false.

Now we can use the authMiddleware with the  flag for the   endpoint.

authentication.controller.ts

Modify the basic logging in logic

So far, the   acts the same way regardless of the 2FA being turned on or not. Let’s modify it by responding just with the   flag if it is turned on. Thanks to that, we don’t give the details of a user just yet. We also avoid sending the  .

Summary

By doing all of the above, we set up a basic flow for the Node.js Two-Factor Authentication. We implement a way to generate a secret key and a QR image, turn on the Two-Factor Authentication, and validate upcoming requests. It might use some tweaks, like additional error handling, and a way for the users to deal with a lost device. This article covers just one way to implement Node.js 2FA. Feel free to change the flow and implement additional features, if needed.

Series Navigation<< TypeScript Express tutorial #10. Testing Express applicationsTypeScript Express tutorial #12. Creating a CI/CD pipeline with Travis and Heroku >>
Subscribe
Notify of
guest
6 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Pablito
Pablito
4 years ago

Really good job Man!

I’d consider increasing width of scrollbar because it’s not useful 🙂

Pablito
Pablito
4 years ago

And one more thing!

More like a suggestion for future 🙂

That would be great if You could show to dockerize this kind of app, altogether with postgres and mongo 🙂

Pablito
Pablito
4 years ago
Reply to  Pablito

Already noticed that it’s done 😀

hieusmiths
4 years ago

Sr you can write tutorial about GraphQl ? thanks

Nguyen Huu Duc
3 years ago

Thanks you so much Bro, There are all great blogs! <3

vietanh
vietanh
2 years ago

Thanks you