Node.js + MongoDB: User Authentication & Authorization with JWT

Node.js & MongoDB User Authentication example

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

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

  • By role (admin, moderator, user), the User has access to protected resources or not

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

Following diagram shows you the flow that we’re gonna implement for User Registration, User Login and Authorization process.

node-js-mongodb-jwt-authentication-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

Here is an overview of our Node.js Express App:

node-js-mongodb-jwt-authentication-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

An error message will be sent as HTTP response to Client when the middlewares throw any error, .

Controllers interact with MongoDB Database via Mongoose library 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

  • mongoose 5.9.1

  • MongoDB

Project Structure

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

node-js-mongodb-jwt-authentication-project-structure

Create Node.js App

Create a folder for our project with command:

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

Let’s install necessary modules such as: express, cors, body-parser, mongoose, jsonwebtoken and bcryptjs. Run the command:

Check package.json file, you can see it looks like this:

Setup Express web server

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

What we’be just done in the code above: – 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-mongodb-jwt-authentication-example-setup-server

Configure MongoDB database

In the app folder, create config folder for configuration.

Then create a new db.config.js file that contains parameters for setting up MongoDB later:

Define the Mongoose Model

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

models/role.model.js

models/user.model.js

These Mongoose Models represents users & roles collections in MongoDB database. User object will have a roles array that contains ids in roles collection as reference.

This kind is called Reference Data Models or Normalization. You can find more details at: MongoDB One-to-Many Relationship tutorial with Mongoose examples

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

  • create a new User: object.save()

  • find a User by id: User.findById(id)

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

  • find User by username: User.findOne({ username: … })

  • find all Roles which name in given roles array: Role.find({ name: { $in: roles } })

These functions will be used in our Controllers and Middlewares.

Initialize Mongoose

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

Open server.js and add following code to open Mongoose connection to MongoDB database:

initial() function helps us to create 3 important rows in roles collection.

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 duplications for username and email – check if roles in the request is legal or not

middlewares/verifySignUp.js

To process Authentication & Authorization, we create following 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 (any role) – /api/test/mod for moderator users – /api/test/admin for admin users

controllers/user.controller.js

Let's combine middlewares with controller functions 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.

The console shows:

Let's check roles collection in MongoDB database:

node-js-mongodb-jwt-authentication-roles-collection

Register some users with /signup API:

  • admin with admin role

  • modera with moderator and user roles

  • bezkoder with user role

node-js-mongodb-jwt-authentication-example-users-registration

users collection after signup could look like this.

node-js-mongodb-jwt-authentication-example-users-collection

Access public resource: GET /api/test/all

node-js-mongodb-jwt-authentication-example-public-resourses

Access protected resource: GET /api/test/user

node-js-mongodb-jwt-authentication-example-protected-resourses

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

node-js-mongodb-jwt-authentication-example-login-wrong-password

Login an legal account: POST /api/auth/signin

node-js-mongodb-jwt-authentication-example-login-successful

Access protected resources: GET /api/test/user

node-js-mongodb-jwt-authentication-example-access-resourses

GET /api/test/admin

node-js-mongodb-jwt-authentication-example-access-admin

If we use a wrong access token:

node-js-mongodb-jwt-authentication-example-wrong-token

Last updated

Was this helpful?