Node.js Token Based Authentication & Authorization example

Overview of Node.js JWT Authentication example

We will build a Node.js Express application in that:

  • User can signup new account, or login with username & password.

  • By User’s role (admin, moderator, user), we authorize the User to access resources

These are APIs that we need to provide:

Methods

Urls

Actions

POST

/api/auth/signup

signup new account

POST

/api/auth/signin

login an account

GET

/api/test/all

retrieve public content

GET

/api/test/user

access User’s content

GET

/api/test/mod

access Moderator’s content

GET

/api/test/admin

access Admin’s content

Flow for Signup & Login with JWT Authentication

The diagram shows flow of User Registration, User Login and Authorization process.

node-js-jwt-authentication-mysql-flow

A legal JWT must be added to HTTP x-access-token Header if Client accesses protected resources.

Node.js Express Architecture with Authentication & Authorization

You can have an overview of our Node.js Express App with the diagram below:

node-js-jwt-authentication-mysql-architecture

Via Express routes, HTTP request that matches a route will be checked by CORS Middleware before coming to Security layer.

Security layer includes:

  • JWT Authentication Middleware: verify SignUp, verify token

  • Authorization Middleware: check User’s roles with record in database

If these middlewares throw any error, a message will be sent as HTTP response.

Controllers interact with MySQL Database via Sequelize and send HTTP response (token, user information, data based on roles…) to client.

Technology

  • Express 4.17.1

  • bcryptjs 2.4.3

  • jsonwebtoken 8.5.1

  • Sequelize 5.21.3

  • MySQL

Project Structure

This is directory structure for our Node.js Express application:

node-js-jwt-authentication-mysql-project-structure

Create Node.js App

First, we create a folder for our project:

Then we initialize the Node.js App with a package.json file:

We need to install necessary modules: express, cors, body-parser, sequelize, mysql2, jsonwebtoken and bcryptjs. Run the command:

The package.json file now looks like this:

Setup Express web server

In the root folder, let’s create a new server.js file:

Let me explain what we’ve just done: – import express, body-parser and cors modules:

  • Express is for building the Rest apis

  • body-parser helps to parse the request and create the req.body object

  • cors provides Express middleware to enable CORS

– create an Express app, then add body-parser and cors middlewares using app.use() method. Notice that we set origin: http://localhost:8081. – define a GET route which is simple for test. – listen on port 8080 for incoming requests.

Now let’s run the app with command: node server.js. Open your browser with url http://localhost:8080/, you will see:

node-js-jwt-authentication-mysql-example-setup-server

Configure MySQL database & Sequelize

In the app folder, create config folder for configuration with db.config.js file like this:

First five parameters are for MySQL connection. pool is optional, it will be used for Sequelize connection pool configuration:

  • max: maximum number of connection in pool

  • min: minimum number of connection in pool

  • idle: maximum time, in milliseconds, that a connection can be idle before being released

  • acquire: maximum time, in milliseconds, that pool will try to get connection before throwing error

For more details, please visit API Reference for the Sequelize constructor.

Define the Sequelize Model

In models folder, create User and Role data model as following code:

models/user.model.js

models/role.model.js

These Sequelize Models represents users & roles table in MySQL database.

After initializing Sequelize, we don’t need to write CRUD functions, Sequelize supports all of them:

  • create a new User: create(object)

  • find a User by id: findByPk(id)

  • find a User by email: findOne({ where: { email: ... } })

  • get all Users: findAll()

  • find all Users by username: findAll({ where: { username: ... } })

These functions will be used in our Controllers and Middlewares.

Initialize Sequelize

Now create app/models/index.js with content like this:

The association between Users and Roles is Many-to-Many relationship: – One User can have several Roles. – One Role can be taken on by many Users.

We use User.belongsToMany(Role) to indicate that the user model can belong to many Roles and vice versa.

With through, foreignKey, otherKey, we’re gonna have a new table user_roles as connection between users and roles table via their primary key as foreign keys.

If you want to know more details about how to make Many-to-Many Association with Sequelize and Node.js, please visit: Sequelize Many-to-Many Association example – Node.js & MySQL

Don’t forget to call sync() method in server.js.

initial() function helps us to create 3 rows in database. In development, you may need to drop existing tables and re-sync database. So you can use force: true as code above.

For production, just insert these rows manually and use sync() without parameters to avoid dropping data:

Learn how to implement Sequelize One-to-Many Relationship at: Sequelize Associations: One-to-Many example – Node.js, MySQL

Configure Auth Key

jsonwebtoken functions such as verify() or sign() use algorithm that needs a secret key (as String) to encode and decode token.

In the app/config folder, create auth.config.js file with following code:

You can create your own secret String.

Create Middleware functions

To verify a Signup action, we need 2 functions: – check if username or email is duplicate or not – check if roles in the request is existed or not

middlewares/verifySignUp.js

To process Authentication & Authorization, we have these functions: - check if token is provided, legal or not. We get token from x-access-token of HTTP headers, then use jsonwebtoken's verify() function. - check if roles of the user contains required role or not.

middlewares/authJwt.js

middlewares/index.js

Create Controllers

Controller for Authentication

There are 2 main functions for Authentication: - signup: create new User in database (role is user if not specifying role) - signin:

  • find username of the request in database, if it exists

  • compare password with password in database using bcrypt, if it is correct

  • generate a token using jsonwebtoken

  • return user information & access Token

controllers/auth.controller.js

Controller for testing Authorization

There are 4 functions: – /api/test/all for public access – /api/test/user for loggedin users (role: user/moderator/admin) – /api/test/mod for users having moderator role – /api/test/admin for users having admin role

controllers/user.controller.js

Now, do you have any question? Would you like to know how we can combine middlewares with controller functions? Let's do it in the next section.

Define Routes

When a client sends request for an endpoint using HTTP request (GET, POST, PUT, DELETE), we need to determine how the server will response by setting up the routes.

We can separate our routes into 2 part: for Authentication and for Authorization (accessing protected resources).

Authentication:

  • POST /api/auth/signup

  • POST /api/auth/signin

routes/auth.routes.js

Authorization:

  • GET /api/test/all

  • GET /api/test/user for loggedin users (user/moderator/admin)

  • GET /api/test/mod for moderator

  • GET /api/test/admin for admin

routes/user.routes.js

Don't forget to add these routes in server.js:

Run & Test with Results

Run Node.js application with command: node server.js

Tables that we define in models package will be automatically generated in MySQL Database. If you check the database, you can see things like this:

Register some users with /signup API:

  • admin with admin role

  • mod with moderator and user roles

  • zkoder with user role

node-js-jwt-authentication-mysql-signup-new-user

Our tables after signup could look like this.

Access public resource: GET /api/test/all

node-js-jwt-authentication-mysql-get-public-content

Access protected resource: GET /api/test/user

node-js-jwt-authentication-mysql-get-user-content-not-loggedin

Login an account (with wrong password): POST /api/auth/signin

node-js-jwt-authentication-mysql-user-signin-wrong-password

Login an account: POST /api/auth/signin

node-js-jwt-authentication-mysql-user-signin

Access protected resources: GET /api/test/user

node-js-jwt-authentication-mysql-get-authorized-content

Last updated

Was this helpful?