Node - Js + MongoDB User Authentication & Authorization With JWT - BezKoder
Node - Js + MongoDB User Authentication & Authorization With JWT - BezKoder
com
Following diagram shows you the flow that we’re gonna implement
for User Registration, User Login and Authorization process.
A legal JWT must be added to HTTP x-access-token Header if
Client accesses protected resources.
Technology
Express 4.17.1
bcryptjs 2.4.3
jsonwebtoken 8.5.1
mongoose 5.9.1
MongoDB
Project Structure
name: (node-js-jwt-auth-mongodb)
version: (1.0.0)
description: Node.js + MongoDB: JWT Authentication
& Authorization
entry point: (index.js) server.js
test command:
git repository:
keywords: node.js, express, jwt, authentication,
mongodb
author: bezkoder
license: (ISC)
var corsOptions = {
origin: "http://localhost:8081"
};
app.use(cors(corsOptions));
// parse requests of content-type -
application/json
app.use(bodyParser.json());
// simple route
app.get("/", (req, res) => {
res.json({ message: "Welcome to bezkoder
application." });
});
module.exports = Role;
models/user.model.js
const mongoose = require("mongoose");
module.exports = User;
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
const db = {};
db.mongoose = mongoose;
db.user = require("./user.model");
db.role = require("./role.model");
module.exports = db;
Open server.js and add following code to open Mongoose
connection to MongoDB database:
...
const app = express();
app.use(...);
const db = require("./app/models");
const Role = db.role;
db.mongoose
.connect(`mongodb://${dbConfig.HOST}:${dbConfig.PORT}
/${dbConfig.DB}`, {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => {
console.log("Successfully connect to
MongoDB.");
initial();
})
.catch(err => {
console.error("Connection error", err);
process.exit();
});
...
function initial() {
Role.estimatedDocumentCount((err, count) => {
if (!err && count === 0) {
new Role({
name: "user"
}).save(err => {
if (err) {
console.log("error", err);
}
new Role({
name: "moderator"
}).save(err => {
if (err) {
console.log("error", err);
}
new Role({
name: "admin"
}).save(err => {
if (err) {
console.log("error", err);
}
if (user) {
res.status(400).send({ message: "Failed!
Username is already in use!" });
return;
}
// Email
User.findOne({
email: req.body.email
}).exec((err, user) => {
if (err) {
res.status(500).send({ message: err });
return;
}
if (user) {
res.status(400).send({ message: "Failed!
Email is already in use!" });
return;
}
next();
});
});
};
checkRolesExisted = (req, res, next) => {
if (req.body.roles) {
for (let i = 0; i < req.body.roles.length;
i++) {
if (!ROLES.includes(req.body.roles[i])) {
res.status(400).send({
message: `Failed! Role
${req.body.roles[i]} does not exist!`
});
return;
}
}
}
next();
};
const verifySignUp = {
checkDuplicateUsernameOrEmail,
checkRolesExisted
};
module.exports = verifySignUp;
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
const jwt = require("jsonwebtoken");
const config = require("../config
/auth.config.js");
const db = require("../models");
const User = db.user;
const Role = db.role;
if (!token) {
return res.status(403).send({ message: "No
token provided!" });
}
Role.find(
{
_id: { $in: user.roles }
},
(err, roles) => {
if (err) {
res.status(500).send({ message: err });
return;
}
Role.find(
{
_id: { $in: user.roles }
},
(err, roles) => {
if (err) {
res.status(500).send({ message: err });
return;
}
const authJwt = {
verifyToken,
isAdmin,
isModerator
};
module.exports = authJwt;
middlewares/index.js
const authJwt = require("./authJwt");
const verifySignUp = require("./verifySignUp");
module.exports = {
authJwt,
verifySignUp
};
Create Controllers
if (req.body.roles) {
Role.find(
{
name: { $in: req.body.roles }
},
(err, roles) => {
if (err) {
res.status(500).send({ message: err
});
return;
}
user.roles = roles.map(role =>
role._id);
user.save(err => {
if (err) {
res.status(500).send({ message: err
});
return;
}
user.roles = [role._id];
user.save(err => {
if (err) {
res.status(500).send({ message: err
});
return;
}
if (!user) {
return res.status(404).send({ message:
"User Not found." });
}
if (!passwordIsValid) {
return res.status(401).send({
accessToken: null,
message: "Invalid Password!"
});
}
Define Routes
module.exports = function(app) {
app.use(function(req, res, next) {
res.header(
"Access-Control-Allow-Headers",
"x-access-token, Origin, Content-Type,
Accept"
);
next();
});
app.post(
"/api/auth/signup",
[
verifySignUp.checkDuplicateUsernameOrEmail,
verifySignUp.checkRolesExisted
],
controller.signup
);
app.post("/api/auth/signin", controller.signin);
};
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
const { authJwt } = require("../middlewares");
const controller = require("../controllers
/user.controller");
module.exports = function(app) {
app.use(function(req, res, next) {
res.header(
"Access-Control-Allow-Headers",
"x-access-token, Origin, Content-Type,
Accept"
);
next();
});
app.get("/api/test/all", controller.allAccess);
app.get("/api/test/user", [authJwt.verifyToken],
controller.userBoard);
app.get(
"/api/test/mod",
[authJwt.verifyToken, authJwt.isModerator],
controller.moderatorBoard
);
app.get(
"/api/test/admin",
[authJwt.verifyToken, authJwt.isAdmin],
controller.adminBoard
);
};
Don't forget to add these routes in server.js:
...
// routes
require('./app/routes/auth.routes')(app);
require('./app/routes/user.routes')(app);
GET /api/test/admin
Further Reading
https://www.npmjs.com/package/express
http://expressjs.com/en/guide/routing.html
In-depth Introduction to JWT-JSON Web Token
https://mongoosejs.com/docs/queries.html
https://mongoosejs.com/docs/api/model.html
Source Code
You can find the complete source code for this tutorial on Github.