Mongodb Authentication in Nodejs
Mongodb Authentication in Nodejs
6.1 Introduction
Node.js is the most popular JavaScript framework when it comes to high-speed application
development. Node.js Professionals often opt for a NoSQL database which can keep up with
Node.js speed all while maintaining the performance of the application. MongoDB is a perfect fit
for this kind of requirement as it has a very fast development cycle and performs quite efficiently.
NoSQL, or most commonly known as Not only SQL database, provides a mechanism for storage
and retrieval of unstructured data. This type of database can handle a humongous amount of data
and has a dynamic schema. So, a NoSQL database has no specific query language, no or a very
few relationships, but has data stored in the format of collections and documents.
So, a database can have a ‘n’ number of collections and each collection can have ‘m‘ number of
documents. Consider the example below.
As you can see from the above image, there is an Employee Database which has 2 collections i.e.
the Employee and Projects Collection. Now, each of these collections has Documents, which are
basically the data values. So, you can assume the collections to be your tables and the Documents
to be your fields in the tables.
There is a list of NoSQL databases which are used quite heavily in the industry, some of which I
have listed below:
1. MongoDB
2. Hbase
3. Cassandra
4. Amazon SimpleDB
5. Hypertable
So, in this face off, I will be comparing both these databases based on the following grounds:
1. Type of Database
2. Schema
3. Database Categories
4. Complex Queries
5. Hierarchical Data Storage
6. Scalability
7. Language
8. Online Processing
9. Base Properties
10. External Support
MongoDB is an open source non-relational database that stores the data in the form of collections
and documents. This kind of databases preserves most of the functionalities while offering
horizontal scalability. This eases the work of a developer by providing persistence to the data and
enhancing agility.
MongoDB stores the JSON documents in the form of collections having dynamic schemas. It
stores all the related information together which enhances the speed of query processing. This way,
it also helps in bringing down the gap between the key-value stores and relational databases.
Below I have listed down a few of the most intriguing features of MongoDB:
Indexing: It makes use of indexes that helps in improving the search performance.
Replication: MongoDB distributes the data across different machines.
Ad-hoc Queries: It supports ad-hoc queries by indexing the BSON documents & using a
unique query language.
Schemaless: It enhances the flexibility of the data and needs no script to modify or update
data.
Sharding: It makes use of sharding which eases the deployment of very large data sets and
provides high throughput operations.
Here I will be creating a CRUD application for Course Management with the help of Node.js and
Express.js and use MongoDB to store the data. In this application, I will be taking course details
like name, id, duration, and fee as inputs. For that, I will be creating a few view files which will
act as an interface. Then in order to handle the data, I will be needing a controller as well which
will help in manipulating the data. Finally, I will be needing a few model files to store the data. So
basically, I will be following an MVC pattern for this application development.
open the command prompt and navigate to your project directory. Now you need to set up the
project configurations for that, type in the below command and provide the necessary details:
1 npm init
Now, you need to install the required packages. So, in this project, I am using the below packages:
1 npm i -g nodemon
Create the file to establish connectivity between Node.js and MongoDB. For that, first, you need
to create a folder inside the project directory and name it ‘model’. Inside this folder, create a
javascript file with the name ‘mongodb.js‘ and type in the below code:
mongodb.js
const mongoose = require('mongoose')
mongoose.connect('mongodb://localhost/mongo-demo', {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => console.log('connected to mongodb successfully....'))
.catch(err =>console.log('failed to connect to mongodb',err));
Now, you need to define the schema of your course database. For that, create a new JS file within
the model folder and name it ‘course.model.js‘. So, I am using four fields in my course object I
am using four fields which are name, id, duration, and fee. To create this file, type in the below-
given code.
course.model.js
mongoose.model('Course', courseSchema);
Now, you need to create the root file called ‘script.js‘. This file is the entry point of this application
and will contain all the connection paths in it. You need to be really careful while providing the
paths in this file as it might result in an error or application failure. Along with this, it is also
responsible for invoking the server and establish the connection. In order to create this file, type
in the below code:
script.js
require('./model/mongodb')
const courseController = require('./controllers/courseController');
app.use(bodyparser.urlencoded({extended: true}));
app.use(bodyparser.json());
//Set the Controller path which will be responding the user actions
app.use('/api/courses', courseController);
Next, in order to handle the user requests, you need to create the router file. For that first, create a
folder and name it ‘controller’ and within this folder create a file with the name
‘courseController.js‘. In this file, we will be dealing with the CRUD operations related to the
employee. Below is the code for creating this file:
courseController.js
});
//Router to update a course using it's ID
router.get('/:id', (req, res) => {
Course.findById(req.params.id)
.then(course => res.send(course))
.catch(err => res.send(err).status(404));
});
NOTE
For details on specific operator, including syntax and examples, click on the specific operator to
go to its reference page.
For comparison of different BSON type values, see the specified BSON comparison order.
Name Description
$gte Matches values that are greater than or equal to a specified value.
$lte Matches values that are less than or equal to a specified value.
$ne Matches all values that are not equal to a specified value.
NOTE
For details on specific operator, including syntax and examples, click on the specific operator to
go to its reference page.
Name Description
Joins query clauses with a logical AND returns all documents that match the conditions
$and
of both clauses.
Name Description
Inverts the effect of a query expression and returns documents that do not match the
$not
query expression.
Joins query clauses with a logical NOR returns all documents that fail to match both
$nor
clauses.
Joins query clauses with a logical OR returns all documents that match the conditions
$or
of either clause.
CHAPTER SEVEN: Authentication and authorization
Authentication is the act of validating that users are who they claim to be. Passwords are the
most common authentication factor—if a user enters the correct password, the system assumes
the identity is valid and grants access.
Other technologies such as One-Time Pins, authentication apps, and even biometrics can also be
used to authenticate identity. In some instances, systems require the successful verification of
more than one factor before granting access. This multi-factor authentication (MFA) requirement
is often deployed to increase security beyond what passwords alone can provide.
Authorization in system security is the process of giving the user permission to access a specific
resource or function. This term is often used interchangeably with access control or client
privilege. Giving someone permission to download a particular file on a server or providing
individual users with administrative access to an application are good examples. In secure
environments, authorization must always follow authentication—users should first prove that
their identities are genuine before an organization’s administrators grant them access to the
requested resources.
Let's use an analogy to outline the differences. If you need to enter a house and the door is
locked, you need a set of keys to open it. Unlocking the door with the correct key and gaining
access to the house verifies you have the right to enter. This process is authentication. The lock
on the door only grants access to someone with the correct key, in much the same way that a
system only grants access to users that have the correct credentials.
However, once you have entered the house, you may not have the owner’s permission to access
certain areas or appliances. Imagine that your neighbor has asked you to feed her pets while she
is away. In this example, you have the authorization to access the kitchen and open the cupboard
storing the pet food. However, you can’t go into your neighbor’s bedroom as she did not
explicitly permit you to do so. Even though you had the right to enter the house (authentication),
your neighbor only allowed you access to certain areas (authorization).
7.4 Hashing Passwords with Node.js and Bcrypt
The bcrypt library on NPM makes it really easy to hash and compare passwords in Node. If
you're coming from a PHP background, these are roughly equivalent
to password_hash() and password_verify().
Installing
Bcrypt supports both sync and async methods. The asynchronous approach is recommended
because hashing is CPU intensive, and the synchronous version will block the event loop and
prevent your app from handling other requests until it finishes.
Thus, while the sync version is more convenient, it's best to stick with async if you're concerned
about performance.
Asynchronous Version
Hashing a password is as simple as this. The second argument is the number of rounds to use
when generating a salt.
Synchronous Version
if(bcrypt.compareSync('somePassword', hash)) {
// Passwords match
} else {
// Passwords don't match
}
Authentication allows your application to know that the person who is sending a request to your
application is actually who they say they are. The JSON web token (JWT) is one method for
allowing authentication, without actually storing any information about the user on the system
itself.
In this lecture, we will demonstrate how JWT based authentication works, and how to build a
sample application in Node.js to implement it.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIxIiwiZXhwIjoxNTQ3
OTc0MDgyfQ.2Ye5_w1z3zpD4dSGdRp3s98ZipCNQqmsHRB9vioOx54
Now the interesting thing is that the header and payload are not encrypted. They are just base64
encoded. This means that anyone can view their contents by decoding them.
{"alg":"HS256","typ":"JWT"}
{"username":"user1","exp":1547974082}
Let’s pretend you’re an application that wants to issue a JWT to a user (for example, user1) that
has successfully signed in.
Making the header and payload are pretty straightforward: The header is more or less fixed, and
the payload JSON object is formed by setting the user ID and the expiry time in unix
milliseconds.
The application issuing the token will also have a key, which is a secret value, and known only to
the application itself. The base64 representations of the header and payload are then combined
with the secret key and then passed through a hashing algorithm (in this case its HS256, as
mentioned in the header)
The details of how the algorithm is implemented is out of scope for this lecture, but the important
thing to note is that it is one way, which means that we cannot reverse the algorithm and obtain
the components that went into making the signature… so our secret key remains secret.
Verifying a JWT
In order to verify an incoming JWT, a signature is once again generated using the header and
payload from the incoming JWT, and the secret key. If the signature matches the one on the
JWT, then the JWT is considered valid.
Now let’s pretend that you’re a hacker trying to issue a fake token. You can easily generate the
header and payload, but without knowing the key, there is no way to generate a valid signature.
If you try to tamper with the existing payload of a valid JWT, the signatures will no longer
match.
In this way, the JWT acts as a way to authorize users in a secure manner, without actually storing
any information (besides the key) on the application server.
Implementation in Node.js
Now that we’ve seen how JWT based authentication works, let’s implement it using Node.
Let’s start by initializing the HTTP server with the required routes in the index.js file. We’ve
used express as the server framework:
app.use(bodyParser.json())
app.use(cookieParser())
app.post('/signin', signIn)
app.get('/welcome', welcome)
app.post('/refresh', refresh)
app.listen(8000)
The /signin route will take the users credentials and log them in. For simplification, we’re storing
the users information as an in-memory map in our code:
const users = {
user1: 'password1',
user2: 'password2'
}
So for now, there are only two valid users in our application: user1, and user2. Next, we can
write the signIn HTTP handler in a new file handlers.js. For this example we are using
the jsonwebtoken library to help us create and verify JWT tokens.
const users = {
user1: 'password1',
user2: 'password2'
return res.status(401).end()
algorithm: 'HS256',
expiresIn: jwtExpirySeconds
})
console.log('token:', token)
// set the cookie as the token string, with a similar max age as the token
res.end()
If a user logs in with the correct credentials, this handler will then set a cookie on the client side
with the JWT value. Once a cookie is set on a client, it is sent along with every request
henceforth. Now we can write our welcome handler to handle user specific information.
Now that all logged in clients have session information stored on their end as cookies, we can
use it to:
// We can obtain the session token from the requests cookies, which come with every request
if (!token) {
return res.status(401).end()
var payload
try {
// Note that we are passing the key in this method as well. This method will throw an error
// if the token is invalid (if it has expired according to the expiry time we set on sign in),
} catch (e) {
if (e instanceof jwt.JsonWebTokenError) {
// if the error thrown is because the JWT is unauthorized, return a 401 error
return res.status(401).end()
return res.status(400).end()
// Finally, return the welcome message to the user, along with their
In this example, we have set a short expiry time of five minutes. We should not expect the user
to login every five minutes if their token expires. To solve this, we will create
another /refresh route that takes the previous token (which is still valid), and returns a new token
with a renewed expiry time.
To minimize misuse of a JWT, the expiry time is usually kept in the order of a few minutes.
Typically the client application would refresh the token in the background.
// (BEGIN) The code uptil this point is the same as the first part of the `welcome` route
if (!token) {
return res.status(401).end()
var payload
try {
} catch (e) {
if (e instanceof jwt.JsonWebTokenError) {
return res.status(401).end()
}
return res.status(400).end()
// (END) The code uptil this point is the same as the first part of the `welcome` route
// We ensure that a new token is not issued until enough time has elapsed
// In this case, a new token will only be issued if the old token is within
return res.status(400).end()
// Now, create a new token for the current user, with a renewed expiration time
algorithm: 'HS256',
expiresIn: jwtExpirySeconds
})
res.end()
module.exports = {
signIn,
welcome,
refresh