├── .gitignore ├── README.md ├── app ├── config │ ├── auth.config.js │ └── db.config.js ├── controllers │ ├── auth.controller.js │ └── user.controller.js ├── middleware │ ├── authJwt.js │ ├── index.js │ └── verifySignUp.js ├── models │ ├── index.js │ ├── role.model.js │ └── user.model.js └── routes │ ├── auth.routes.js │ └── user.routes.js ├── jwt-refresh-token-node-js-example-flow.png ├── jwt-token-authentication-node-js-example-flow.png ├── package.json └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node.js – JWT Authentication & Authorization example with JSONWebToken & Sequelize 2 | 3 | ## User Registration, User Login and Authorization process. 4 | The diagram shows flow of how we implement User Registration, User Login and Authorization process. 5 | 6 | ![jwt-token-authentication-node-js-example-flow](jwt-token-authentication-node-js-example-flow.png) 7 | 8 | For more detail, please visit: 9 | > [Node.js JWT Authentication & Authorization example](https://bezkoder.com/node-js-jwt-authentication-mysql/) 10 | 11 | You may need to implement Refresh Token: 12 | 13 | ![jwt-refresh-token-node-js-example-flow](jwt-refresh-token-node-js-example-flow.png) 14 | 15 | > [Node.js JWT Refresh Token example](https://bezkoder.com/jwt-refresh-token-node-js/) 16 | 17 | Working with Front-end: 18 | > [Vue](https://www.bezkoder.com/jwt-vue-vuex-authentication/) 19 | 20 | > [Angular 8](https://www.bezkoder.com/angular-jwt-authentication/) / [Angular 10](https://www.bezkoder.com/angular-10-jwt-auth/) / [Angular 11](https://www.bezkoder.com/angular-11-jwt-auth/) / [Angular 12](https://www.bezkoder.com/angular-12-jwt-auth/) / [Angular 13](https://www.bezkoder.com/angular-13-jwt-auth/) 21 | 22 | > [React](https://www.bezkoder.com/react-jwt-auth/) / [React + Redux](https://www.bezkoder.com/react-redux-jwt-auth/) 23 | 24 | ## More Practice: 25 | > [Build Node.js Rest APIs with Express, Sequelize & MySQL](https://bezkoder.com/node-js-express-sequelize-mysql/) 26 | 27 | > [Server side Pagination in Node.js with Sequelize and MySQL](https://bezkoder.com/node-js-sequelize-pagination-mysql/) 28 | 29 | > [Node.js Express File Upload Rest API example](https://bezkoder.com/node-js-express-file-upload/) 30 | 31 | > [Node.js Express File Upload with Google Cloud Storage example](https://bezkoder.com/google-cloud-storage-nodejs-upload-file/) 32 | 33 | > [Node.js JWT Authentication & Authorization example with MongoDB](https://bezkoder.com/node-js-mongodb-auth-jwt/) 34 | 35 | Associations: 36 | > [Sequelize Associations: One-to-Many Relationship example](https://bezkoder.com/sequelize-associate-one-to-many/) 37 | 38 | > [Sequelize Associations: Many-to-Many Relationship example](https://bezkoder.com/sequelize-associate-many-to-many/) 39 | 40 | Deployment: 41 | > [Docker Compose: Node.js Express and MySQL example](https://www.bezkoder.com/docker-compose-nodejs-mysql/) 42 | 43 | > [Deploying/Hosting Node.js app on Heroku with MySQL database](https://bezkoder.com/deploy-node-js-app-heroku-cleardb-mysql/) 44 | 45 | Integration on same Server/Port: 46 | > [Integrate Vue with Node.js Express](https://www.bezkoder.com/serve-vue-app-express/) 47 | 48 | > [Integrate Angular with Node.js Express](https://www.bezkoder.com/integrate-angular-12-node-js/) 49 | 50 | > [Integrate React with Node.js Express](https://www.bezkoder.com/integrate-react-express-same-server-port/) 51 | 52 | ## Project setup 53 | ``` 54 | npm install 55 | ``` 56 | 57 | ### Run 58 | ``` 59 | node server.js 60 | ``` 61 | -------------------------------------------------------------------------------- /app/config/auth.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | secret: "bezkoder-secret-key" 3 | }; 4 | -------------------------------------------------------------------------------- /app/config/db.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | HOST: "localhost", 3 | USER: "root", 4 | PASSWORD: "123456", 5 | DB: "testdb", 6 | dialect: "mysql", 7 | pool: { 8 | max: 5, 9 | min: 0, 10 | acquire: 30000, 11 | idle: 10000 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /app/controllers/auth.controller.js: -------------------------------------------------------------------------------- 1 | const db = require("../models"); 2 | const config = require("../config/auth.config"); 3 | const User = db.user; 4 | const Role = db.role; 5 | 6 | const Op = db.Sequelize.Op; 7 | 8 | var jwt = require("jsonwebtoken"); 9 | var bcrypt = require("bcryptjs"); 10 | 11 | exports.signup = (req, res) => { 12 | // Save User to Database 13 | User.create({ 14 | username: req.body.username, 15 | email: req.body.email, 16 | password: bcrypt.hashSync(req.body.password, 8) 17 | }) 18 | .then(user => { 19 | if (req.body.roles) { 20 | Role.findAll({ 21 | where: { 22 | name: { 23 | [Op.or]: req.body.roles 24 | } 25 | } 26 | }).then(roles => { 27 | user.setRoles(roles).then(() => { 28 | res.send({ message: "User registered successfully!" }); 29 | }); 30 | }); 31 | } else { 32 | // user role = 1 33 | user.setRoles([1]).then(() => { 34 | res.send({ message: "User registered successfully!" }); 35 | }); 36 | } 37 | }) 38 | .catch(err => { 39 | res.status(500).send({ message: err.message }); 40 | }); 41 | }; 42 | 43 | exports.signin = (req, res) => { 44 | User.findOne({ 45 | where: { 46 | username: req.body.username 47 | } 48 | }) 49 | .then(user => { 50 | if (!user) { 51 | return res.status(404).send({ message: "User Not found." }); 52 | } 53 | 54 | var passwordIsValid = bcrypt.compareSync( 55 | req.body.password, 56 | user.password 57 | ); 58 | 59 | if (!passwordIsValid) { 60 | return res.status(401).send({ 61 | accessToken: null, 62 | message: "Invalid Password!" 63 | }); 64 | } 65 | 66 | const token = jwt.sign({ id: user.id }, 67 | config.secret, 68 | { 69 | algorithm: 'HS256', 70 | allowInsecureKeySizes: true, 71 | expiresIn: 86400, // 24 hours 72 | }); 73 | 74 | var authorities = []; 75 | user.getRoles().then(roles => { 76 | for (let i = 0; i < roles.length; i++) { 77 | authorities.push("ROLE_" + roles[i].name.toUpperCase()); 78 | } 79 | res.status(200).send({ 80 | id: user.id, 81 | username: user.username, 82 | email: user.email, 83 | roles: authorities, 84 | accessToken: token 85 | }); 86 | }); 87 | }) 88 | .catch(err => { 89 | res.status(500).send({ message: err.message }); 90 | }); 91 | }; 92 | -------------------------------------------------------------------------------- /app/controllers/user.controller.js: -------------------------------------------------------------------------------- 1 | exports.allAccess = (req, res) => { 2 | res.status(200).send("Public Content."); 3 | }; 4 | 5 | exports.userBoard = (req, res) => { 6 | res.status(200).send("User Content."); 7 | }; 8 | 9 | exports.adminBoard = (req, res) => { 10 | res.status(200).send("Admin Content."); 11 | }; 12 | 13 | exports.moderatorBoard = (req, res) => { 14 | res.status(200).send("Moderator Content."); 15 | }; 16 | -------------------------------------------------------------------------------- /app/middleware/authJwt.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | const config = require("../config/auth.config.js"); 3 | const db = require("../models"); 4 | const User = db.user; 5 | 6 | verifyToken = (req, res, next) => { 7 | let token = req.headers["x-access-token"]; 8 | 9 | if (!token) { 10 | return res.status(403).send({ 11 | message: "No token provided!" 12 | }); 13 | } 14 | 15 | jwt.verify(token, 16 | config.secret, 17 | (err, decoded) => { 18 | if (err) { 19 | return res.status(401).send({ 20 | message: "Unauthorized!", 21 | }); 22 | } 23 | req.userId = decoded.id; 24 | next(); 25 | }); 26 | }; 27 | 28 | isAdmin = (req, res, next) => { 29 | User.findByPk(req.userId).then(user => { 30 | user.getRoles().then(roles => { 31 | for (let i = 0; i < roles.length; i++) { 32 | if (roles[i].name === "admin") { 33 | next(); 34 | return; 35 | } 36 | } 37 | 38 | res.status(403).send({ 39 | message: "Require Admin Role!" 40 | }); 41 | return; 42 | }); 43 | }); 44 | }; 45 | 46 | isModerator = (req, res, next) => { 47 | User.findByPk(req.userId).then(user => { 48 | user.getRoles().then(roles => { 49 | for (let i = 0; i < roles.length; i++) { 50 | if (roles[i].name === "moderator") { 51 | next(); 52 | return; 53 | } 54 | } 55 | 56 | res.status(403).send({ 57 | message: "Require Moderator Role!" 58 | }); 59 | }); 60 | }); 61 | }; 62 | 63 | isModeratorOrAdmin = (req, res, next) => { 64 | User.findByPk(req.userId).then(user => { 65 | user.getRoles().then(roles => { 66 | for (let i = 0; i < roles.length; i++) { 67 | if (roles[i].name === "moderator") { 68 | next(); 69 | return; 70 | } 71 | 72 | if (roles[i].name === "admin") { 73 | next(); 74 | return; 75 | } 76 | } 77 | 78 | res.status(403).send({ 79 | message: "Require Moderator or Admin Role!" 80 | }); 81 | }); 82 | }); 83 | }; 84 | 85 | const authJwt = { 86 | verifyToken: verifyToken, 87 | isAdmin: isAdmin, 88 | isModerator: isModerator, 89 | isModeratorOrAdmin: isModeratorOrAdmin 90 | }; 91 | module.exports = authJwt; 92 | -------------------------------------------------------------------------------- /app/middleware/index.js: -------------------------------------------------------------------------------- 1 | const authJwt = require("./authJwt"); 2 | const verifySignUp = require("./verifySignUp"); 3 | 4 | module.exports = { 5 | authJwt, 6 | verifySignUp 7 | }; 8 | -------------------------------------------------------------------------------- /app/middleware/verifySignUp.js: -------------------------------------------------------------------------------- 1 | const db = require("../models"); 2 | const ROLES = db.ROLES; 3 | const User = db.user; 4 | 5 | checkDuplicateUsernameOrEmail = (req, res, next) => { 6 | // Username 7 | User.findOne({ 8 | where: { 9 | username: req.body.username 10 | } 11 | }).then(user => { 12 | if (user) { 13 | res.status(400).send({ 14 | message: "Failed! Username is already in use!" 15 | }); 16 | return; 17 | } 18 | 19 | // Email 20 | User.findOne({ 21 | where: { 22 | email: req.body.email 23 | } 24 | }).then(user => { 25 | if (user) { 26 | res.status(400).send({ 27 | message: "Failed! Email is already in use!" 28 | }); 29 | return; 30 | } 31 | 32 | next(); 33 | }); 34 | }); 35 | }; 36 | 37 | checkRolesExisted = (req, res, next) => { 38 | if (req.body.roles) { 39 | for (let i = 0; i < req.body.roles.length; i++) { 40 | if (!ROLES.includes(req.body.roles[i])) { 41 | res.status(400).send({ 42 | message: "Failed! Role does not exist = " + req.body.roles[i] 43 | }); 44 | return; 45 | } 46 | } 47 | } 48 | 49 | next(); 50 | }; 51 | 52 | const verifySignUp = { 53 | checkDuplicateUsernameOrEmail: checkDuplicateUsernameOrEmail, 54 | checkRolesExisted: checkRolesExisted 55 | }; 56 | 57 | module.exports = verifySignUp; 58 | -------------------------------------------------------------------------------- /app/models/index.js: -------------------------------------------------------------------------------- 1 | const config = require("../config/db.config.js"); 2 | 3 | const Sequelize = require("sequelize"); 4 | const sequelize = new Sequelize( 5 | config.DB, 6 | config.USER, 7 | config.PASSWORD, 8 | { 9 | host: config.HOST, 10 | dialect: config.dialect, 11 | pool: { 12 | max: config.pool.max, 13 | min: config.pool.min, 14 | acquire: config.pool.acquire, 15 | idle: config.pool.idle 16 | } 17 | } 18 | ); 19 | 20 | const db = {}; 21 | 22 | db.Sequelize = Sequelize; 23 | db.sequelize = sequelize; 24 | 25 | db.user = require("../models/user.model.js")(sequelize, Sequelize); 26 | db.role = require("../models/role.model.js")(sequelize, Sequelize); 27 | 28 | db.role.belongsToMany(db.user, { 29 | through: "user_roles" 30 | }); 31 | db.user.belongsToMany(db.role, { 32 | through: "user_roles" 33 | }); 34 | 35 | db.ROLES = ["user", "admin", "moderator"]; 36 | 37 | module.exports = db; 38 | -------------------------------------------------------------------------------- /app/models/role.model.js: -------------------------------------------------------------------------------- 1 | module.exports = (sequelize, Sequelize) => { 2 | const Role = sequelize.define("roles", { 3 | id: { 4 | type: Sequelize.INTEGER, 5 | primaryKey: true 6 | }, 7 | name: { 8 | type: Sequelize.STRING 9 | } 10 | }); 11 | 12 | return Role; 13 | }; 14 | -------------------------------------------------------------------------------- /app/models/user.model.js: -------------------------------------------------------------------------------- 1 | module.exports = (sequelize, Sequelize) => { 2 | const User = sequelize.define("users", { 3 | username: { 4 | type: Sequelize.STRING 5 | }, 6 | email: { 7 | type: Sequelize.STRING 8 | }, 9 | password: { 10 | type: Sequelize.STRING 11 | } 12 | }); 13 | 14 | return User; 15 | }; 16 | -------------------------------------------------------------------------------- /app/routes/auth.routes.js: -------------------------------------------------------------------------------- 1 | const { verifySignUp } = require("../middleware"); 2 | const controller = require("../controllers/auth.controller"); 3 | 4 | module.exports = function(app) { 5 | app.use(function(req, res, next) { 6 | res.header( 7 | "Access-Control-Allow-Headers", 8 | "x-access-token, Origin, Content-Type, Accept" 9 | ); 10 | next(); 11 | }); 12 | 13 | app.post( 14 | "/api/auth/signup", 15 | [ 16 | verifySignUp.checkDuplicateUsernameOrEmail, 17 | verifySignUp.checkRolesExisted 18 | ], 19 | controller.signup 20 | ); 21 | 22 | app.post("/api/auth/signin", controller.signin); 23 | }; 24 | -------------------------------------------------------------------------------- /app/routes/user.routes.js: -------------------------------------------------------------------------------- 1 | const { authJwt } = require("../middleware"); 2 | const controller = require("../controllers/user.controller"); 3 | 4 | module.exports = function(app) { 5 | app.use(function(req, res, next) { 6 | res.header( 7 | "Access-Control-Allow-Headers", 8 | "x-access-token, Origin, Content-Type, Accept" 9 | ); 10 | next(); 11 | }); 12 | 13 | app.get("/api/test/all", controller.allAccess); 14 | 15 | app.get( 16 | "/api/test/user", 17 | [authJwt.verifyToken], 18 | controller.userBoard 19 | ); 20 | 21 | app.get( 22 | "/api/test/mod", 23 | [authJwt.verifyToken, authJwt.isModerator], 24 | controller.moderatorBoard 25 | ); 26 | 27 | app.get( 28 | "/api/test/admin", 29 | [authJwt.verifyToken, authJwt.isAdmin], 30 | controller.adminBoard 31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /jwt-refresh-token-node-js-example-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezkoder/node-js-jwt-auth/6aa349e1df0692440f398f321150450be77847f1/jwt-refresh-token-node-js-example-flow.png -------------------------------------------------------------------------------- /jwt-token-authentication-node-js-example-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bezkoder/node-js-jwt-auth/6aa349e1df0692440f398f321150450be77847f1/jwt-token-authentication-node-js-example-flow.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-js-jwt-auth", 3 | "version": "1.0.0", 4 | "description": "Node.js Demo for JWT Authentication", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "node.js", 11 | "jwt", 12 | "authentication", 13 | "express", 14 | "mysql" 15 | ], 16 | "author": "bezkoder", 17 | "license": "ISC", 18 | "dependencies": { 19 | "bcryptjs": "^2.4.3", 20 | "cors": "^2.8.5", 21 | "express": "^4.18.2", 22 | "jsonwebtoken": "^9.0.0", 23 | "mysql2": "^2.3.3", 24 | "sequelize": "^6.32.1" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const cors = require("cors"); 3 | 4 | const app = express(); 5 | 6 | var corsOptions = { 7 | origin: "http://localhost:8081" 8 | }; 9 | 10 | app.use(cors(corsOptions)); 11 | 12 | // parse requests of content-type - application/json 13 | app.use(express.json()); 14 | 15 | // parse requests of content-type - application/x-www-form-urlencoded 16 | app.use(express.urlencoded({ extended: true })); 17 | 18 | // database 19 | const db = require("./app/models"); 20 | const Role = db.role; 21 | 22 | db.sequelize.sync(); 23 | // force: true will drop the table if it already exists 24 | // db.sequelize.sync({force: true}).then(() => { 25 | // console.log('Drop and Resync Database with { force: true }'); 26 | // initial(); 27 | // }); 28 | 29 | // simple route 30 | app.get("/", (req, res) => { 31 | res.json({ message: "Welcome to bezkoder application." }); 32 | }); 33 | 34 | // routes 35 | require('./app/routes/auth.routes')(app); 36 | require('./app/routes/user.routes')(app); 37 | 38 | // set port, listen for requests 39 | const PORT = process.env.PORT || 8080; 40 | app.listen(PORT, () => { 41 | console.log(`Server is running on port ${PORT}.`); 42 | }); 43 | 44 | function initial() { 45 | Role.create({ 46 | id: 1, 47 | name: "user" 48 | }); 49 | 50 | Role.create({ 51 | id: 2, 52 | name: "moderator" 53 | }); 54 | 55 | Role.create({ 56 | id: 3, 57 | name: "admin" 58 | }); 59 | } --------------------------------------------------------------------------------