๐Ÿ”ด No Passport.js - Node.js API Authentication with Typescript ๐Ÿ‘

This tutorial is an introduction point to understand how simple it is to build a JWT authentication system with Node.js and Express.js without using third-party libraries like Passport.js and services like Google Firebase or Auth0. The backend is built using Express.js for the server, TypeORM for Data access layer, and the routing-controllers library to create Routes and Controllers using Decorators

Welcome back for another tutorial where I will explain how to secure your REST API without using the Passport.js library. This tutorial is an introduction point to understand how the authentication and authorization flow is performed behind the scenes by third-party libraries like Passport.js and services like Google Firebase or Auth0, as there are some developers that don’t really understand the process of authentication.

Recently I was working on a personal project where I had to implement the JWT authentication and authorization functionality. I come to a decision to realize it without the help of Passport.js or any other third-party library, so I can discover by myself how the JSON Web Token Authentication can be achieved.

The backend is built using Express.js for the server, TypeORM for Data access layer, and the routing-controllers library to create Routes and Controllers using Decorators in the same approach of Spring Boot.

In this tutorial we will not see how to setup Typescript with an Express.js project, so it is supposed that you already know how to do it. Or you can use the TypeORM CLI tool to generate a Typescript application, after installing TypeORM globally. They have a very detailed Documentation you can refer to.

In this post we will focus essentially on the Authentication.

Requirements ๐Ÿ“‹

After setting up the Node.js project with Express.js, Typescript, TypeORM, and routing-controllers, install the following dependencies using this command.

npm install -s helmet jsonwebtoken bcryptjs
#or
yarn add helmet jsonwebtoken bcryptjs

helmet: Adds different HTTP headers to secure our application.

jsonwebtoken: Provides the jwt operations like generation and verification of token.

bcryptjs: Used to hash user passwords.

Since we are working with Typescript, let’s install the type definitions for our dependencies and save them as dev dependencies.

npm install -D @types/helmet @types/jsonwebtoken @types/bcryptjs
#or
yarn add @types/helmet @types/jsonwebtoken @types/bcryptjs -D

Installing the type check dependencies will give us the opportunity to use the autocomplete and typecheck features of Typescript.

Project Structure ๐Ÿงฌ

node js express js api jwt authentication with typescript

Create an Authentication Controller ๐Ÿ‘ฎ

Inside the controllers directory create a typescript file AuthController.ts which will hold the Signin, and Signup process for users, as well as some other secured methods to test if the JWT authentication is working.

@JsonController('/auth')
export class AuthController {}

Create a Sign-Up Method ๐Ÿ”“

@JsonController('/auth')
export class AuthController {

    constructor(private readonly authService: AuthService) {}

    @Post("/signup")
    async signUp(@EntityFromBody() user: User) {
        
        const password = user.password;
        const passwordHashed = this.hashPassword(password);
        user.password = passwordHashed;
        
        let savedUser: User = await this.userService.create(user);

        const token = this.generateJwt(savedUser);

        return {
            user: {
                userId: savedUser.id, username:savedUser.username, role:savedUser.role
            },
            token
        };    
    }

    hashPassword(password: string) {
        password =  bcrypt.hashSync(password, 12);

        return password;
    }
}

As you can see here the signup method hash the user password before storing user details in the database, then it generates the JSON Web Token and returns it as a result.

Create a Sign-In Method ๐Ÿ”

@JsonController('/auth')
export class AuthController {

    @Post("/signin")
    async signIn(@BodyParam("username") username: string, @BodyParam("password") password: string) {

        //Get user from database
        let user: User;
        try {
            user = await this.userService.getWithPassword(username);
        } catch (error) {
            throw new UnauthorizedError("Username incorrect")
        }

        //Check if encrypted password match
        if (!this.isPasswordCorrect(password, user.password)) {
            throw new UnauthorizedError("Password Incorrect")
        }

        //Sing JWT, valid for 1 hour
        const token = this.generateJwt(user);

        return {
            user: {
                userId: user.id, username:user.username, role:user.role
            },
            token
        };
    }

    isPasswordCorrect(password: string, savedPassword: string): boolean{ 

        if(bcrypt.compareSync(password, savedPassword)) return true;
        else return false;
    }
}

When the user wants to login, this is what happens:

  • The user sends its credentials (username and password)
  • The server tries to find the user in the database using the provided username
  • If the user exists, the server compares the sent password and the one stored
  • If the comparison is valid, the server sends back a JSON Web Token (JWT) to the user

Then, the JWT key had to be sent with every user’s request to access the protected endpoints.

Generate a JWT in Node.js ๐Ÿงฐ

Let’s create the generateJwt method which will generate the JSON Web Token using the jsonwebtoken library as follow.

import jwt from "jsonwebtoken"

generateJwt(user: User) {
        return jwt.sign({data:{userId:user.id, username:user.username, role:user.role}}, 
                         config.jwtSecret, { expiresIn: '6h' });
}

A golden rule is to keep your JWT Secret in a safe place; otherwise your whole authentication system will be compromised.

Create an Authentication Middleware Checker ๐Ÿ›ก️

Inside the middlewares directory create the CheckJWT.ts file which will hold the logic that checks if the user is authenticated or not.

import { Request, Response, NextFunction } from "express";
import jwt from "jsonwebtoken";
import config from "../config/config";

const extractTokenFromHeader = (req: Request) => {
  if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
      return req.headers.authorization.split(' ')[1];
    }
}

export const checkJwt = (req: Request, res: Response, next: NextFunction) => {
  
  //extract the jwt token from the Authorization header
  const token =  extractTokenFromHeader(req);
  let jwtPayload;
  
  //Try to validate the token and get data
  try {
    jwtPayload = jwt.verify(token, config.jwtSecret);
    res.locals.jwtPayload = jwtPayload;
  } catch (error) {
    //If token is not valid, respond with 401 (unauthorized)
    res.status(401).send({response: "You should be logged in to access this url"});
    return;
  }

  //We refresh the token on every request by setting another 1h
  const {data:{ userId, username, role }} = jwtPayload;
  const newToken = jwt.sign({data:{ userId, username, role }}, config.jwtSecret, {
    expiresIn: "1h"
  });
  res.setHeader("Authorization", 'Bearer ' + newToken);

  next();
}

This Middleware has to be placed or called before reaching any protected endpoint. The Routing-controllers library provides the @UseBefore() decorator especially for this purpose. You can place it on the controller level or method level, where you want the middleware to be executed before the requested action.

Create an Authorization Middleware Checker ⛔

The Routing-controllers package also offers the @Authorized() decorator which help implement an authorization system based on the user’s roles.

So, inside the middlewares directory create the CheckRole.ts file which will contain the logic that checks if the user has the required role to access an endpoint or not.

import { getCustomRepository } from "typeorm";
import { User } from "../db/entity/User";
import { Action } from "routing-controllers";
import { UserRepository } from "../db/repository/UserRepository";

export const checkRole = async (action: Action, roles: Array) => {
  
    //Get the Express Response object from Routing-controllers Action
    let res = action.response;
    //Get the user ID stored on the response object by the checkJwt middleware
    const id = res.locals.jwtPayload.userId;

    //Find user role in the database
    const userRepository = getCustomRepository(UserRepository);
    let user: User;
    try {
      user = await userRepository.findOneOrFail(id);
    } catch (id) {
      return false;
    }

    if (user && !roles.length)
        return true;
    //Check if array of authorized roles includes the user's role
    if (roles.indexOf(user.role) > -1) return true;
    else return false;;
};

For the @Authorized decorator to work you need to configure the authorizationChecker option. You can have more details about the Routing-controllers library by referring to their Repository.

createConnection().then(async connection => {

    useExpressServer(app, { // register created express server in routing-controllers
        controllers: [AuthController],
        authorizationChecker: checkRole// and configure it the way you need (controllers, validation, etc.)
    });

    app.listen(config.port, () => console.log(`Listening on port ${config.port}`));
}).catch(error => console.log("Error: ", error));

Now, you can put the decorator on the controller actions and specify the roles needed to access these endpoints like so @Authorized(["admin","user"]).

Secure Endpoints and Verify JWT Authentication and Authorization ๐Ÿงช

Head back to the AuthController and add a protected endpoint to test if everything is working as expected.

@Get("/protected")
    @UseBefore(checkJwt)
    async protectedEP() {

        let username = res.locals.jwtPayload.data.username;
        return `You successfully reached This protected endpoint Mr: ${username}`;
   }

If the JWT is valid you will get a response from the server with a message returned by the protectedEP() method, otherwise you will be Unauthorized to access this endpoint.

@Get("/admin")
    @UseBefore(checkJwt)
    @Authorized(["admin"])
    async adminEP() {

        let username = res.locals.jwtPayload.data.username;
        return `You successfully reached This protected endpoint Mr: ${username} because you're an admin`;
    }

If the authenticated user has the required role, he will receive a message returned from the adminEP() method, otherwise he will get a Forbidden error message.

Takeaway ๐Ÿ“ฆ

While it is always recommended to opt for existing third-party libraries and services for authentication, so we don’t reinvent the wheel and save a huge development time, it is also important to understand the mystery behind JWT authentication and how it works.

In this article we demonstrated how simple it is to build a JWT authentication system with Node.js and Express.js without using third-party libraries like Passport.js or services like Google Firebase.

I hope you have learned something new today, until then stay tuned.

Name

Angular,7,Angular 8,1,Best Practices,1,Design,1,Firebase,1,Ionic,1,Java,5,node,1,Nodejs,2,Python,1,Restful API,1,Software Development,1,Spring,3,Spring Batch,1,Spring Boot 2,1,Typescript,1,Web Development,1,
ltr
item
Programming Tutorials, News and Reviews: ๐Ÿ”ด No Passport.js - Node.js API Authentication with Typescript ๐Ÿ‘
๐Ÿ”ด No Passport.js - Node.js API Authentication with Typescript ๐Ÿ‘
This tutorial is an introduction point to understand how simple it is to build a JWT authentication system with Node.js and Express.js without using third-party libraries like Passport.js and services like Google Firebase or Auth0. The backend is built using Express.js for the server, TypeORM for Data access layer, and the routing-controllers library to create Routes and Controllers using Decorators
https://1.bp.blogspot.com/-TPciblIvswQ/XgZBqmlfETI/AAAAAAAAAZw/gMy8MplPs20XIQ0G83t2mu3rBwUZ4aQJwCLcBGAsYHQ/s200/no-passport-js-%2Bexpress-js-api-%2Bauthentication-with-typeorm-typescript.jpg
https://1.bp.blogspot.com/-TPciblIvswQ/XgZBqmlfETI/AAAAAAAAAZw/gMy8MplPs20XIQ0G83t2mu3rBwUZ4aQJwCLcBGAsYHQ/s72-c/no-passport-js-%2Bexpress-js-api-%2Bauthentication-with-typeorm-typescript.jpg
Programming Tutorials, News and Reviews
https://www.ninjadevcorner.com/2019/12/no-passportjs-nodejs-rest-api-authentication-with-typescript.html
https://www.ninjadevcorner.com/
https://www.ninjadevcorner.com/
https://www.ninjadevcorner.com/2019/12/no-passportjs-nodejs-rest-api-authentication-with-typescript.html
true
493653397416713395
UTF-8
Loaded All Posts Not found any posts VIEW ALL Readmore Reply Cancel reply Delete By Home PAGES POSTS View All RECOMMENDED FOR YOU LABEL ARCHIVE SEARCH ALL POSTS Not found any post match with your request Back Home Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sun Mon Tue Wed Thu Fri Sat January February March April May June July August September October November December Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec just now 1 minute ago $$1$$ minutes ago 1 hour ago $$1$$ hours ago Yesterday $$1$$ days ago $$1$$ weeks ago more than 5 weeks ago Followers Follow THIS CONTENT IS PREMIUM Please share to unlock Copy All Code Select All Code All codes were copied to your clipboard Can not copy the codes / texts, please press [CTRL]+[C] (or CMD+C with Mac) to copy