How to create an Authentication Module with JWT in NestJs

demola malomo
13 min readJan 2, 2021
Authentication

Preliminary

As a typical web developer, there are times when we need some form of protection against unauthorized access to information on our application. Companies like Okta, AuthO e.t.c, offer authentication-as-a-service with tons of documentation on how it can be integrated into applications (frontend or backend).

I recently kicked off my journey into backend development and I figured at some point, I might be required to build or maintain a custom authentication codebase.

In this article, I will show you how to create a custom authentication module with JWT in NestJs.

Why NestJs?

There are lots of libraries and frameworks built on javascript that enables us to create server-side applications. However, I have found NestJs to be a better option for the following reason:

  • It is a progressive Node.js framework for building a scalable application
  • Good modular architecture for the project
  • Allows integration of third-party libraries
  • Uses ExpressJs under the wood

Prerequisites

We will be needing the following:

  • Nodejs installed on your PC (Follow this link for installation steps)
  • NestJs CLI setup on your PC (You can use this link for installation steps)
  • PostgreSQL and PgAdmin installed (You can try out other databases of your choice)
  • Postman installed (For testing endpoints)
  • Basic knowledge of Typescript and PostgreSQL (or any other database)

You can check my repository for this project
https://github.com/Mr-Malomz/auth-nestjs

Setting up NestJs

Step 1.
Navigate to a preferred project location, open your terminal, and scaffold a new project using Nest CLI.

Step 2.
Select the preferred package manager for the installation(npm or yarn), and change the directory to the project folder using the command below

scaffolding project

Nest CLI creates a project directory, node modules, and a few other boilerplate files, and a src/ directory will be created and populated with several core files.

Let’s go over some of the directories and files from above:

  • node_modules: collection of libraries downloaded from npm.
  • src/app.controller.ts: a basic controller with a single route. Controllers generally handle incoming and outgoing requests.
  • src/app.controller.spec.ts: for writing unit-test (beyond the scope of this tutorial).
  • src/app.module.ts: the root module of the application. Modules in programming are used to divide your programs into smaller units.
  • src/app.service.ts: a class used to abstract all business logic away from the controller. Service helps us keep our controller clean(processing only request).
  • test/app.e2e-spec.ts: for writing end-to-end tests (beyond the scope of this tutorial).

Step 3.
Open your terminal and run the project usingnpm run start:dev

Open http://localhost:3000 on your preferred browser

You should see “Hello World” on your browser if everything goes well

Scaffold Auth Module

We need to scaffold our auth module using Nest CLI by running the command below.

scaffolding auth module

The module will serve as an entry point for configuring properties related to our authentication.

On successful running of the command, an auth folder (stores all files related to auth) will be created.auth.module.ts contains an Authmodule class for registering services, controller, imports e.t.c.

auth folder and auth.module.ts
Auth folder and Auth module scaffolded by nest CLI
AuthModule Class in auth.module.ts

Nest CLI automatically updates app.module.ts with AuthModule Class

Updated app.module.ts

Next, we need to create a controller and service to handle requests and abstract the controller’s logic respectively. Good news! Nest CLI can scaffold these files for us.

scaffolding auth controller and service
Updated auth folder with controller and service

Database Setup

We need a database to save authentication information.
To set up our application database, PostgreSQL and PgAdmin have to be installed.
To begin, we open our PgAdmin. It should look like this.

PS: PostgreSQL default password is postgres if it has not been changed.

PgAdmin landing screen

Next, right-click on the database and create a new database “authuser”, then save.

creating and saving database

Object Relational Mapping and TypeORM
Object Relational Mapping (ORM) is a programming technique used for querying and manipulating data from a database. ORM lets you use a programming language to interact with a database by writing little or no SQL.

TypeORM is an ORM library that runs in Node.js and can be used with TypeScript or JavaScript.

Now that we have covered the definition of ORM and TypeORM, we will now proceed to connect our nest.js application to our database.

First, we need to install the required dependencies using your terminal

installing typeorm and postgres

@nestjs/typeorm:It is a Nestjs wrapper that helps integrate TypeORM.

typeorm:This installs TypeORM.

pg:This installs PostgreSQL driver used by TypeORM to connect to the database.

PS: TypeORM also supports other database integration.

Now that our dependencies have been successfully installed, we need to initialize the database connection in our root module (app.module.ts). Before doing this, let us create a config folder and a TypeScript file to save our configuration.
Create a folder inside src folder called config and then create a file typeorm.config.ts.
Your folder structure should look like this

Config folder and typeorm.config.ts file

Open thistypeorm.config.ts file, then copy and paste the configuration code below

typeorm.config.ts

Let’s explain this code a bit

TypeORMModuleOptions:an interface that shows us all options related to configuring our database.

type:Database type. “postgres” since we are using PostgreSQL. This will inform TypeORM to use the pg driver we installed earlier.

host:“localhost” since we shall be testing locally.

port:5432 is PostgreSQL database port.

username:“postgres” by default.

password:“postgres” by default. If changes have been made to postgreSQL database password, then the password should be used instead of the default password.

database:“authuser”. The name of the database we created in PgAdmin earlier.

entities:Entities represent tables in our database. So we provided a regular expression for typeORM to match any folder and files that end with entity.ts in our project directory.

synchronize:“true” means it should sync up with the schema in the postgreSQL database.

Next, we need to import typeORMConfig created earlier and use it in our app.module.ts to complete our configuration.

updated app.module.ts with TypeOrmConfig

Run the application using npm run star:dev.

If you notice any error on the terminal, please check the article and make corrections accordingly.

Entity
Entities represent tables on the database.
Let's now create a user entity file user.entity.ts in our auth directory. Updated folder structure

user.entity.ts

Now open user.entity.ts and input the code below

User Entity

Let’s explain this code a bit:

We started by creating a User class that extends BaseEntity (a class that lets us inherit methods and properties needed for creating an entity provided by TypeORM).
Next, we decorated the class with @Entity() and @Unique([‘email’]). This decorator is used to mark classes that represent a table and also mark columns with unique values (Email in our case).

Next, we defined the columns and properties of the table. Columns can be PrimaryGeneratedColumn, Column e.t.c. They also allow properties like length, regular expression check, nullability, and so on.

Repository Design Pattern
Repository Pattern is a design pattern that hides the details of how data are stored and retrieved from the database. It abstracts the details of data access logic from business logic.

Now let's create a repository file user.repository.ts in our auth directory. Updated folder structure

user.repository.ts

Open user.repository.ts and input the code below

User Repository

Let’s explain this code a bit:

First, we created a class UserRepository to handle data logic and extend it with TypeORM built-in class Repository that takes in an entity(User in our case).
The decorator @EntityRepository() informs TypeORM that we are creating a custom repository.

To make this repository available in our auth module, we need to inform Nest.js of the repository we intend using in our module by updating the import in auth.module.ts. This makes our repository available for use via injection throughout our module.

app.module.ts

Now we can navigate to auth.service.ts file and inject UserRepository using dependency injection method. This is a technique that allows an object(auth service) receives information from another object(UserRepository) it depends on.
Updated code in auth.service.ts

auth.service.ts

The newly created private property called userRepository gives us access to methods and properties associated with our user repository created earlier. With this, we can now start writing our authentication functionality.

Sign Up Process with Authentication

There are steps we need to follow when incorporating authentication in the signup process.

a. Repository
We need to update user.repository.ts with an async method to handle all data logic needed for sign up. Typically, the signup process involves getting parameters like username, first name, email e.t.c. from users, validating, and storing them on the database. We will make use of email, password, first name, and last name for this tutorial.
We will be needing these parameters in ouruser.repository.ts auth.service.ts and auth.controller.ts . We will also create a Data Transfer Object (DTO) to represent them.

Data Transfer Object (DTO) is simply an object that transfers data from one point to another. You can read more here.

Next, we create DTO to handle sign up parameters.
1. Create a DTO folder in the auth folder.
2. Create a auth-signup.dto.ts file and input the code in the gist auth-signup.dto.ts

Updated Folder Structure
auth-signup.dto.ts

We currently do not have any form of validation in our DTO and we need to change that. Nest.js uses Pipes to transform(convert from one data type to another) and validate(evaluate input data for its validity) user’s responses.
Nest.js also supports class-validator library. This powerful library allows us to use decorator-based validation.
We need to install the required dependency before we can start using it.

class validator installation

Now, using a class-validator, we will validate our parameters to ensure that the data sent to the endpoint is in an acceptable format.
Update auth-signup.dto.ts as follows

auth-signup.dto.ts

We can now use this DTO in user.repository.ts

Updated user.repositry.ts

Let’s update user.repository.ts with an asynchronous signUp method.
This method takes a parameter of type AuthSignUpDto which we created earlier and returns a promised-based string since our method is asynchronous.
For this, we destructured the authSignUpDto to get the email, password, firstname, and lastname. We also created a new user instance from our User entity class and save destructed parameters accordingly.
Lastly, we wrap the user instance to the database with a try and catch block. We also checked for duplicate email entries using the error code provided by TypeOrm and used an inbuilt exception class provided by Nestjs to handle errors thrown during operation.

With that out of the way, we now have access to the signUp method anywhere we inject (auth.service.ts in our case) user repository.

b. Service
Now we can use our user repository to create a signUp method:

Updated auth.service.ts

c. Controller
We will use the updated service to create methods in our auth.controller.ts file:

auth.controller.ts

We injected our AuthService class edited earlier by creating a private property authService, then we created an asynchronous signUp method that takes in a parameter of type AuthSignUpDto.
This method has two decorators; @Post(‘/signup’) and @Body(). The first decorator informs Nestjs about the HTTP method type of Post and routes to our api endpoint auth/signup while the second decorator extract parameter from the request object.

d. Postman and PostgreSQL
We will make POST requests to our endpoint using postman and check postgreSQL to see if the data sent is been stored on the database.

endpoint testing on postman
user table with data stored on PostgreSQL

e. Hashing Passwords
From the previous section d, you will notice that the password is still stored as plain text. This is not what we want. We want to have an extra security on the saved password and we can achieve this by hashing and using random salt on the passwords.
Hashing involves transforming our password into a different string while Salt is the addition of a random string per user to the existing hashed password.
To hash our password, we need to install the required dependency using the command below:

bcrypt installation

PS: bcrypt is the main package while @types/bcrypt helps with typescript support.

Next, we will update user.entity.ts to include an extra column to store random salt per user:

updated user.entity.ts

Next, we will create a private method in user.repository.tsthat takes in two parameters (password and salt) and then returns an encrypted password to be stored in the database:

encrypting password method in user.repository.ts

With the private method in place, we can now hash and salt our password before storing it in the database.
First, we generate a random salt using bcrypt inbuilt genSalt() method and then encrypt the password using the newly generated salt.

updated user.repository.ts

Finally, we will delete our user table on the database by right-clicking on the user table and selecting Delete/Drop option:

delete user table on postgresql

Now let’s test our code by re-starting our application using npm run start:dev and making a request with Postman.
On refreshing the database, we should see user table populated with newly created user details:

user table with hashed password and salt

Sign In Process with Authentication

Now that we have concluded our sign-up process, we will handle the sign-in operation for a registered user.

a. Repository
Create a new DTO to handle processes related to sign-in in our repository, service, and controller:

updated folder with auth-signin.dto.ts
auth-signin.dto.ts

Sign-In Response Type
During authentication, we might be required to send user’s details along with the token. To do this, we need to create a new dto (SignInResponse) to represent our response type:

new folder structure
auth-signinresponse.dto.ts

Next, create a new method in user.repository.ts to handle all database activities related to sign-in.
To compare user passwords and limit the rate at which we send requests to the database, let’s create a method in user.entity.tsto help with validation :

updated user.entity.ts

We imported bcrypt into our user entity and created a validatePassword method that receives an incoming password as a parameter, hash the password with the salt in the database, compares it with the saved password, and returns either true or false match.

user.repository.ts

Next, we update user.repository.ts file by importing the required DTO’s (AuthSignInDto and SignInResponse) and then create a signIn method that takes in data of type AuthSignInDto and returns data of type SignInResponse.
We will then destructure the DTO to get email and password from the request body.
Next, find the user by email using typeORM in-built method findOne() that takes an object of email.
Then we check if the user exists and the password matches the password in the database using the validatePassword method created earlier. If it does, we populated the response accordingly and if it doesn’t we return null.

b. Service
Now we can use signIn method created earlier in theauth.service.ts file by creating a method in the service class to handle siginin. Before we create this method, we need to create an interface to represent how our response is returned when users are authenticated:

user-jwt-response.interface.ts

c. Passport.js and JWT
To complete the sign-in process we need to install some packages to help us with security and token:

install passport and jwt

Let’s go over the packages above:

  • @nestjs/jwt: a wrapper provided by nestjs to ease the use of jwt.
  • @nestjs/passport: a wrapper provided by nestjs to ease the use of passportjs during authentication.
  • passport: an authentication middleware.
  • passport-jwt: a passport strategy for authenticating with jwt.

Now we can start configuring our application to use installed packages.
First, we need to update auth.module.ts file:

update auth.module.ts

Next, we imported the needed method from @nestjs/passport and @nestjs/jwt. We used PassportModule to register the kind of authentication strategy to use (jwt in our case), then JwtModule was used to register our secret key, and also configure signOption for the token to last for 1 hour (3600 seconds).

PS: It’s not advisable to hard-code secrets in your application. Using an environment variable is advisable for this.

Since we have added jwt module in auth.module.ts . We can inject jwtService in our auth.service.ts file:

updated auth.service.ts

We created a signIn method that takes in a parameter of type AuthSignInDto and returns a response of type UserJwtResponse. Remember we created both the DTO and the interface earlier.
Then we will create a variable user response referencing the signIn method created in our repository earlier.
We then conditionally check the response of userResponse, if it doesn’t return a user, we throw an UnauthorizedException with invalid credentials.
But if it does, we create a payload variable and pass the response as an object in it.
Next, we created another variable accessToken to generate a token for this user using the injected jwtService.
Finally, we created a variable signInResponse of type UserJwtResponse and pass in our user response and accesstoken respectively. Then we pass it as the return type for our signIn service.

d. Controller
We can now use the updated service to create signIn method in our auth.controller.ts file:

updated auth.controller.ts

First, we imported the required DTO and interface.
Then we created a signIn method decorated with Post Http method and “/signin” route.
We also passed in a parameter of type AuthSignInDto and returns data of type UserJwtResponse from our service.

Now, we can test our signIn endpoint using any of our created user in the database:

testing signin endpoint

Conclusion
The authentication module works as expected and can be applied to real projects.
However, this module can be extended further and used alongside other modules that need authentication.

You can check my repository for this project
https://github.com/Mr-Malomz/auth-nestjs

If you liked this post, Feedback, claps, and comments are highly appreciated.

You can connect with me on Twitter and LinkedIn

References
https://docs.nestjs.com/
https://nodejs.org/en/download/
https://www.postgresql.org/download/
https://www.pgadmin.org/download/
https://www.postman.com/downloads/
https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping
https://typeorm.io/#/
https://en.wikipedia.org/wiki/Data_transfer_object
https://github.com/typestack/class-validator
https://docs.nestjs.com/pipes

--

--

demola malomo

A Tech Enthusiast | I constantly learn new software concept and blog about it.