Nest JS: Understanding and using JWT token for route security | 2024

Amir Mustafa
6 min readJan 21, 2024

→ In today’s article we will understand all about JWT Token. We will then use it in our Nest.js application

→ Nest.js is a Node JS framework like Express.js

What is a JWT Token:

→ JWT token is in applications for security routes against attacks.

→ It is open source Industry standard (RFC-7519)

→ It is used for the secure exchange of data between parties.

What does JWT Token look like?

JWT Token is divided into three parts:

Header — Contains metadata about the token(type, hashing algorithm)

Payload — contains claims i.e. statements about entity eg. User info

Signature — Is a result of the encoded header, encoded body, and signed against a secret

Eg:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Check the official website here.

Why is it widely used when storing passwords in DB?

→ When a real John Doe comes at the time of signup after login he can access all his routes i.e. app pages

→ Suppose a fake John Doe comes. He is a regular user and not an admin.

→ If he somehow from Network pulled the payload. He manually changes the roles to admin and tries to access routes that he does not have permissions

→ Fake John Doe will fail as he will not know that other than header and payload — there is a signature (it can be a string or key file) and routes are protected. Hence, the app was saved.

Implementing in Nest.js:

STEP 1: Installing SDK

yarn add @nestjs/jwt @nestjs/passport passport passport-jwt @types/passport-jwt

or

npm install @nestjs/jwt @nestjs/passport passport passport-jwt @types/passport-jwt

STEP 2: Registering JWT Token in Auth Module:

Path: src/auth/auth.module.ts

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { JwtStrategy } from './jwt.strategy';

@Module({
imports: [ // REGISTERING PASSPORT AND JWT
PassportModule.register({defaultStrategy: 'jwt'}),
JwtModule.register({
secret: 'topSecret92', // SECRET KEY - TEXT OR FILE
signOptions: {
expiresIn: 3600 // TOKEN EXPIRY TIME
}
}),
TypeOrmModule.forFeature([User])
],
providers: [AuthService, JwtStrategy],
controllers: [AuthController],
exports: [JwtStrategy,PassportModule]
})
export class AuthModule {}

STEP 3: JWT Strategy:

J→ Create a file inside the auth component.

→ On this page we write the logic for getting and validating tokens from the Postman Bearer token

Path: src/auth/jwt.strategy.ts

import { PassportStrategy } from "@nestjs/passport";
import { InjectRepository } from "@nestjs/typeorm";
import { ExtractJwt, Strategy } from "passport-jwt";
import { User } from "./user.entity";
import { Repository } from "typeorm";
import { JwtPayload } from "./jwt-payload.interface";
import { UnauthorizedException } from "@nestjs/common";

export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>
) {
super({
secretOrKey: 'topSecret92',
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
});
}

async validate(payload: JwtPayload): Promise<User> {
const { username } = payload;
const user: User = await this.userRepository.findOne({select: ["id", "username", "password"], where: {username}});

if(!user) {
throw new UnauthorizedException();
}

return user;
}
}

→ Once we register JWT, we have access to dependency injection in the auth service

File: AuthService.ts

Path: project/src/auth/auth.service.ts

import {
ConflictException,
Injectable,
InternalServerErrorException,
UnauthorizedException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { AuthCredentialsDto } from './dto/auth-credentials.dto';
import { User } from './user.entity';
import { Repository } from 'typeorm';
import * as bcrypt from 'bcrypt';
import { JwtService } from '@nestjs/jwt'; // JWT
import { JwtPayload } from './jwt-payload.interface'; // JWT

@Injectable()
export class AuthService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
private jwtService: JwtService
) {}

async signIn(authcredentialsDto: AuthCredentialsDto): Promise<{ accessToken: string }> {
const { username, password } = authcredentialsDto;
const user = await this.userRepository.findOne({ select: ["id", "username", "password"], where: {username} });

if(user && await bcrypt.compare(password, user.password)) {
// PREVIOUSLY HERE ONLY successful message
// JWT TOKEN FOR SECURE
const payload: JwtPayload = { username };
const accessToken = this.jwtService.sign(payload);
return { accessToken };
} else {
throw new UnauthorizedException('Please check your login credentials.');
}
}
}

File: AuthController:

Path: project/src/auth/auth.controller.ts

→ Here we replace the return type string with the object of accessToken

import { Body, Controller, Post, Req, UseGuards } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthCredentialsDto } from './dto/auth-credentials.dto';

@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}

@Post('/signin')
signIn(@Body() authCredentialsDto: AuthCredentialsDto): Promise<{ accessToken: string }> {
return this.authService.signIn(authCredentialsDto);
}
}

→ That’s all about access token registering in Auth

Guarding a Controller of Routes:

→ Now in the Nest.js src file there will be multiple components — functionalities/features of your application.

→ Now if you want to guard your controller, the first step is to import AuthModule in your Nest.js component where you want to guard your route

Eg. Task Component

Path: project/src/tasks/tasks.module.ts

import { Module } from '@nestjs/common';
import { TasksController } from './tasks.controller';
import { TasksService } from './tasks.service';
// import { TaskRepository } from './tasks.repository';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Task } from './task.entity';
import { AuthModule } from 'src/auth/auth.module'; // Auth Module Imported

@Module({
imports: [TypeOrmModule.forFeature([Task]), AuthModule], // AUTH Module Added
controllers: [TasksController],
providers: [TasksService],
})
export class TasksModule {}

→ Now go to the controller of your same component

Path: src/tasks/tasks.controller.ts

import {
Body,
Controller,
Get,
Param,
Post,
Delete,
Patch,
Query,
UseGuards,
} from '@nestjs/common';
import { TasksService } from './tasks.service';
import { CreateTaskDto } from './dto/create-task.dto';
import { GetTasksFilterDto } from './dto/get-tasks.dto';
import { UpdateTaskStatusDto } from './dto/update-task-status.dto';
import { Task } from './task.entity';
import { AuthGuard } from '@nestjs/passport'; // IMPORT AUTHGUARD

@Controller('tasks')
@UseGuards(AuthGuard()). // THIS LINE GUARDS YOUR ROUTES
export class TasksController {
constructor(private taskService: TasksService) {}

@Get()
getTasks(@Query() filterDto: GetTasksFilterDto): Promise<Task[]> {
return this.taskService.getTasks(filterDto);
}

@Get('/:id')
getTaskById(@Param('id') id: any): Promise<Task> {
return this.taskService.getTaskById(id);
}

@Delete('/:id')
deleteTaskById(@Param('id') id: string): Promise<void> {
return this.taskService.deleteTaskById(id);
}

@Post()
createTask(@Body() createTaskDto: CreateTaskDto): Promise<Task> {
return this.taskService.createTask(createTaskDto);
}

@Patch('/:id/status')
updateTaskStatus(
@Param('id') id: string,
@Body() updateTaskStatus: UpdateTaskStatusDto
): Promise<Task> {
const { status } = updateTaskStatus;
return this.taskService.updateTaskStatus(id, status);
}
}

Postman:

→ Let us try using Postman:

Running Get All Task API:

API: Get All Tasks:

→ We get an unauthorized error — running without token or expired token

API: Signin API

→ Sign in and generate a JWT token. Copy the token

→ Back to the first request pasting the new token and hitting API

Conclusion:

JWT Token is a way to add extra security to your application. Your backend routes become secure. A token always expires and a fresh login is required for successful operation in apps.

There are many other ways for login for example Microsoft SSO Authenticator, etc. When it comes to storing passwords in a database hashing combined with JWT token is famous and used worldwide across multiple programming languages.

Thank you for reading till the end 🙌 . If you enjoyed this article or learned something new, support me by clicking the share button below to reach more people and/or subscribe Happy Learnings !! to see some other tips, articles, and things I learn about and share there.

--

--

Amir Mustafa

JavaScript Specialist | Consultant | YouTuber 🎬. | AWS ☁️ | Docker 🐳 | Digital Nomad | Human. Connect with me on https://www.linkedin.com/in/amirmustafa1/