How to Develop Two-Factor Email Authentication Using NodeJS, PostgreSQL and Nodemailer.

Alex Prozorov
The Startup
Published in
6 min readOct 10, 2020

--

Learn How to run a Postgres DB Docker container, connect an interface and run a NodeJS server that performs two-factor authentication for registering users.

image by https://stockup.sitebuilderreport.com/

There are a few ways to create a two factor authentication in your application. Each website that has user register & login capability should have a two factor authentication for improved security. For Example if you have an Angular client and you want to validate each user who registers on your website then an email/mobile phone authentication is the way to go.

In this article we will cover email Two-Factor implementation with NodeJS server side, PostgreSQL DB running in a docker container and Angular 9 client.

Introduction

We need some kind of a mechanism that allows us to verify a user’s identity. The way to do it is to create a verification code that will be sent for each new user that registers to our website.

Prerequisites

  • NodeJS
  • Angular or any other client-side framework
  • VSCode
  • Docker CLI
  • PostgreSQL

Authentication Process

Important: JSON Web Tokens (JWT)

JSON web tokens are text strings that can be used by client and server to authenticate and share information easily. In JWT, a token is encoded from a data payload using a secret. That token is passed to the client. Whenever the client sends that token along with a request, the server validates it and sends back the response. You can read more about it here at: https://jwt.io/introduction/

In our application each request is validated with a JSON web token which is generated each time a user logs in our website.

Lets summarize the entire process of registering, verifying and logging in to our website.

  • User registers with username, password and email
  • The server creates a user object in the DB
  • A JWT token and a verification code is generated and saved in the DB
  • A Verification URL is send to the email the user registered with containing the JWT token and the verification code
  • User clicks the verification link in his mail box
  • Request in sent to the server, JWT is checked in the middleware
  • If JWT is valid the request gets passed to the verification api endpoint, code is checked, if the verification process is successful the user’s identity is now verified
  • User logs in and can make requests to our website

Setting Up the DB

The first thing we need is an instance of Postgres DB where all our user data will be saved.

Make you sure you have a running Postgres DB running, in this guide we’ll teach you how to get a running Postgres container running on Docker CLI.

On your Centos/Ubuntu OS with Docker CLI installed do the following:

Create a directory where we will create our docker-compose.yml.

[root@localhost postgres]# cd /var/lib/docker/data
[root@localhost data]# mkdir postgres

Install vim editor if you don’t have one.

[root@localhost data]# yum install vim -y

Lets create a docker-compose.yml to run a PostgreSQL image which can be found here.

[root@localhost data]# vim docker-compose.yml

Press the Insert button to type inside the file and type the following:

version: '3.7'
services:
database:
image: "postgres"
env_file:
- database.env
ports:
- target: 5432
published: 5432
protocol: tcp
mode: host
volumes:
- ./database-data:/var/lib/postgresql/data/

This will run a PostgreSQL container, we expose it on port 5432 and use an environment configuration file. We will use a local volume to save the data which is stored in the DB. Lets save the file and create a database.env file which will store our DB login credentials.

[root@localhost data]# vim database.env
POSTGRES_USER=admin
POSTGRES_PASSWORD=123123
POSTGRES_DB=test

This is our connection credentials, save the file and type:

[root@localhost data]# docker-compose up -d

Congrats, We now have a Postgres DB running container!

We connect to our DB using pgAdmin web interface with the following credentials that we specified in the database.env file.

Now we can see the DB in our list

NodeJS Server

Let us start with the package.json. We need the following libraries in our app.

  • Express JS — For serving requests
  • jsonwebtoken — For writing and verifying JWT tokens
  • bcryptjs — For password encryption
  • cryptoRandomString — Generate random verification codes
  • dotenv — Using .env files as config for our server
  • nodemailer and nodemailer-express-handlebars — sending emails
  • pgtools — DB creation
  • Sequelize — promise-based Node.js ORM for Postgres

The server runs on port 4000 in our case, it holds the config variables in a .env file, app.js holds the main initialization logic. controllers/authorization holds register, login and verification logic. The models folder includes the user and verificationToken models and services/initService.js holds the DB tables creation function.

.
├── .env
├── middleware.js
├── package-lock.json
├── package.json
└── app.js
└── node_modules
└──controllers/authorization
└──auth.js
└──verification.js
└──email_templates
└──veification.hbs.js
└──models
└──user.js
└──verificationToken.js
└──services
└──initService.js
└──verificationService.js

Now have the server running, the app.js file containing the main startup logic.

The middleware checks for the validity of the JWT token for every incoming request , but lets take a look at the function which handles the register request.

Register

The function does four main things:

  • Encrypts the provided password and creates a user entity in the DB
  • Creates a verification code
  • Creates a JWT token that will be send with the email activation link
  • Send a verification mail

Now lets register using a simple form including username, password and email. In this case we used an Angular client, but any framework will do just fine.

We have used Sequelize which is a promise-based Node.js ORM for Postgres and other SQL Databases. This ORM makes it easier to query the DB by translating rows in the tables in a PostgreSQL database to objects in the program. If we pay attention there are now two entities in the DB: The user which was created in the users table, and a verificationCode which is in the verification_tokens table.

The verification token was saved in order for us to manage generated tokens and delete expired tokens if necessary.

The three main advantages of using Sequelize versus native SQL are:

  1. Sequelize provides an abstraction layer on top of PostgreSQL that eliminates the need to use SQL query.
  2. Functions may be attached to Models in Sequelize. This allows for seamless incorporation of new functionality.
  3. Queries use function chaining rather than embedded mnemonics which result in code that is more flexible and readable, therefore more maintainable as well.

Verification

Once we get a successful response, we can go to email box we just used to register and an activation email should be there. The Activation email was sent with nodemailer and nodemailer-express-handlebars npm packages. The activation link looks something like this:

http://localhost:4200/verification?token=tEgBm0RrkIkSBx4kD0XW&email=test@gmail.com&jwtToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFsZXhAYmlzLXRlYy5jby5pbCIsImlhdCI6MTYwMjM0ODUwNiwiZXhwIjoxNjAyMzUyMTA2fQ.i6GBON7eM3oxFKll6NWFULPkxGIvOHjb6ATa45f5xZ8

The link includes the JWT token, provided email and a verification code. Now when the user clicks the link he is redirected to the /verification client route and a verification request is sent to the server, if the process is successful a success message is received and the user is now verified.

Client Confirmation Page

The client which runs on port 4200 is redirected to the verification page and the generated link that was received is automatically sent to the server for confirmation.

Verification Endpoint

Once the request gets to the server the email and code is validated, and the user is now verified.

next time the user logs in, he can browse the website as he is now verified.

Conclusion

This one way of building a NodeJS server that includes a two factor authentication mechanism, there are other ways that includes email or mobile authentication methods. The server project can be found at my Github repository. Thank you for reading!

--

--