├── README.md ├── app.js ├── app ├── .DS_Store ├── controllers │ ├── .DS_Store │ ├── admin │ │ └── admin.controller.js │ └── user │ │ └── user.controller.js ├── functions │ └── middleware.function.js ├── lib │ └── config.js ├── models │ ├── admin.model.js │ ├── sqlAdapter.js │ └── user.model.js └── routes │ ├── .DS_Store │ ├── admin-api │ └── admin.routes.js │ ├── index.routes.js │ └── user-api │ └── user.routes.js └── package.json /README.md: -------------------------------------------------------------------------------- 1 | # Nodejs API Authorization 2 | 3 | [![Star Count](https://img.shields.io/badge/dynamic/json?color=brightgreen&label=Star&query=stargazers_count&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fhelloakn%2Fapi-authorization-nodejs-mysql)](https://github.com/helloakn/api-authorization-nodejs-mysql) [![Licence](https://img.shields.io/badge/dynamic/json?color=informational&label=LICENCE&query=license.name&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fhelloakn%2Fapi-authorization-nodejs-mysql)](https://github.com/helloakn/api-authorization-nodejs-mysql) [![Language](https://img.shields.io/badge/dynamic/json?color=blueviolet&label=Language&query=language&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fhelloakn%2Fapi-authorization-nodejs-mysql)](https://github.com/helloakn/api-authorization-nodejs-mysql) 4 | 5 | Tutorial How to Create API with multi route prefixs as well as Multi Authorization in NodeJs 6 | ## Installation 7 | ``` 8 | npm install 9 | ``` 10 | # .env 11 | modify database information in .env as the following sample information. 12 | ``` 13 | SVR_PORT=3003 14 | SVR_IP="0.0.0.0" 15 | DB_HOST=localhost 16 | DB_PORT=3306 17 | DB_USER=urusername 18 | DB_PASSWORD=urpassword 19 | DB_NAME=jwt_tutorial 20 | ALLOW_FROM=http://localhost:3000% 21 | ``` 22 | **ALLOW_FROM** is for admin dashboard application 23 | ## Run the program 24 | ``` 25 | npm run start 26 | ``` 27 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require("express"); 3 | const cors = require("cors"); 4 | const config = require('./app/lib/config.js'); 5 | 6 | 7 | // prefixing the multi routes 8 | express.application.prefix = express.Router.prefix = function (path, configure) { 9 | var router = express.Router(); 10 | this.use(path, router); 11 | configure(router); 12 | return router; 13 | }; 14 | // end prefixing the multi routes 15 | 16 | const app = express(); 17 | var corsOptions = { 18 | origin: config.Origin.allowFrom 19 | }; 20 | app.use(cors(corsOptions)); 21 | // Parse URL-encoded bodies (as sent by HTML forms) 22 | 23 | app.use( 24 | express.urlencoded({ 25 | extended: true 26 | }) 27 | ) 28 | 29 | app.use(express.json()) 30 | 31 | 32 | // simple route 33 | app.post("/test", function(req,res) { 34 | console.log(req.body); 35 | res.json({ message: "This is testing route" }); 36 | }); 37 | 38 | require("./app/routes/index.routes.js")(app); 39 | 40 | const PORT = process.env.SVR_PORT || 8080; 41 | app.listen(PORT, () => { 42 | console.log(`Server is running on port ${PORT}.`); 43 | }); -------------------------------------------------------------------------------- /app/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helloakn/api-authorization-nodejs-mysql/474f56f472b012cb26b3f289a79ceaef2baeb0d4/app/.DS_Store -------------------------------------------------------------------------------- /app/controllers/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helloakn/api-authorization-nodejs-mysql/474f56f472b012cb26b3f289a79ceaef2baeb0d4/app/controllers/.DS_Store -------------------------------------------------------------------------------- /app/controllers/admin/admin.controller.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | const jwt = require('jsonwebtoken'); 3 | const config = require('../../lib/config.js'); 4 | const AdmineModel = require("../../models/admin.model.js"); 5 | const UserModel = require("../../models/user.model.js"); 6 | let adminModel = new AdmineModel(); 7 | let userModel = new UserModel(); 8 | 9 | exports.login = async function loginFunction(req, res){ 10 | // ---::: Beign Variable Declaration :::--- // 11 | const data = req.body; 12 | const errors = {}; 13 | // ---::: END Variable Declaration :::--- // 14 | if (!req.body) { 15 | res.status(400).send({ 16 | message: "Content can not be empty!" 17 | }); 18 | } 19 | if (data 20 | && Object.keys(data).length === 0 21 | && Object.getPrototypeOf(data) === Object.prototype){ 22 | 23 | errors.data = ['validation failed.']; 24 | } 25 | // ---::: Begin Validation :::--- // 26 | if (!String(req.body.email).trim()) { 27 | errors.email = ['Email is required']; 28 | } 29 | console.log('p'); 30 | if (!data.password) { 31 | errors.password = ['Password is required']; 32 | } 33 | if (!String(req.body.password).trim()) { 34 | errors.password = ['Password is required']; 35 | } 36 | console.log('end p'); 37 | // Begin to return with output 38 | if (Object.keys(errors).length) { 39 | res.status(400).send({ 40 | code:400, 41 | status: "failed!", 42 | error:errors 43 | }); 44 | return; 45 | } 46 | // ---::: END Validation :::--- // 47 | 48 | // validation is passed 49 | let _passwordHash = crypto.createHash('md5').update(data.password).digest("hex"); 50 | let adminRecord = await adminModel.getByEmailAndPassword( 51 | data.email, 52 | _passwordHash 53 | ); 54 | console.log(_passwordHash); 55 | if(adminRecord){ 56 | //generate JWT here 57 | const token = jwt.sign({ adminAccount: adminRecord,authType:"admin" }, config.Secret.admin, { expiresIn: '7d' }); 58 | res.send({ 59 | code:200, 60 | status : "success!", 61 | data :{ 62 | admin_id:adminRecord.id, 63 | email:adminRecord.email, 64 | name:adminRecord.name, 65 | token:token 66 | } 67 | }); 68 | 69 | } 70 | else{ 71 | res.status(400).send({ 72 | code:400, 73 | status: "failed!", 74 | error:{"msg":["Incorrected Login Information!"]} 75 | }); 76 | return; 77 | } 78 | }; 79 | 80 | 81 | exports.setupUser = async function setupUserFunction(req, res){ 82 | const data = req.body; 83 | const errors = {}; 84 | // ---::: END Variable Declaration :::--- // 85 | if (!req.body) { 86 | res.status(400).send({ 87 | message: "Content can not be empty!" 88 | }); 89 | } 90 | if (data 91 | && Object.keys(data).length === 0 92 | && Object.getPrototypeOf(data) === Object.prototype){ 93 | 94 | errors.data = ['validation failed.']; 95 | } 96 | // ---::: Begin Validation :::--- // 97 | if (!String(req.body.email).trim()) { 98 | errors.email = ['Email is required']; 99 | } 100 | if (!data.name) { 101 | errors.name = ['Name is required']; 102 | } 103 | if (!String(req.body.name).trim()) { 104 | errors.name = ['Name is required']; 105 | } 106 | console.log('p'); 107 | if (!data.password) { 108 | errors.password = ['Password is required']; 109 | } 110 | if (!String(req.body.password).trim()) { 111 | errors.password = ['Password is required']; 112 | } 113 | console.log('end p'); 114 | // Begin to return with output 115 | if (Object.keys(errors).length) { 116 | res.status(400).send({ 117 | code:400, 118 | status: "failed!", 119 | error:errors 120 | }); 121 | return; 122 | } 123 | // ---::: END Validation :::--- // 124 | 125 | // validation is passed 126 | let _passwordHash = crypto.createHash('md5').update(data.password).digest("hex"); 127 | let userRecord = await userModel.create({ 128 | "name" : req.body.name, 129 | "email" : req.body.email, 130 | "password" : _passwordHash, 131 | }); 132 | 133 | res.status(200).send({ 134 | code:200, 135 | status: "success!", 136 | data:userRecord 137 | }); 138 | return; 139 | }; -------------------------------------------------------------------------------- /app/controllers/user/user.controller.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | const jwt = require('jsonwebtoken'); 3 | const config = require('../../lib/config.js'); 4 | const UserModel = require("../../models/user.model.js"); 5 | let userModel = new UserModel(); 6 | 7 | exports.login = async function getDetailFunction(req, res){ 8 | // ---::: Beign Variable Declaration :::--- // 9 | const data = req.body; 10 | const errors = {}; 11 | // ---::: END Variable Declaration :::--- // 12 | if (!req.body) { 13 | res.status(400).send({ 14 | message: "Content can not be empty!" 15 | }); 16 | } 17 | if (data 18 | && Object.keys(data).length === 0 19 | && Object.getPrototypeOf(data) === Object.prototype){ 20 | 21 | errors.data = ['validation failed.']; 22 | } 23 | // ---::: Begin Validation :::--- // 24 | if (!String(req.body.email).trim()) { 25 | errors.email = ['Email is required']; 26 | } 27 | console.log('p'); 28 | if (!data.password) { 29 | errors.password = ['Password is required']; 30 | } 31 | if (!String(req.body.password).trim()) { 32 | errors.password = ['Password is required']; 33 | } 34 | console.log('end p'); 35 | // Begin to return with output 36 | if (Object.keys(errors).length) { 37 | res.status(400).send({ 38 | code:400, 39 | status: "failed!", 40 | error:errors 41 | }); 42 | return; 43 | } 44 | // ---::: END Validation :::--- // 45 | 46 | // validation is passed 47 | let _passwordHash = crypto.createHash('md5').update(data.password).digest("hex"); 48 | let userRecord = await userModel.getByEmailAndPassword( 49 | data.email, 50 | _passwordHash 51 | ); 52 | if(userRecord){ 53 | //generate JWT here 54 | const token = jwt.sign({ userAccount: userRecord,authType:"user" }, config.Secret.user, { expiresIn: '7d' }); 55 | res.send({ 56 | code:200, 57 | status : "success!", 58 | data :{ 59 | id:userRecord.id, 60 | email:userRecord.email, 61 | token:token 62 | } 63 | }); 64 | 65 | } 66 | else{ 67 | res.status(400).send({ 68 | code:400, 69 | status: "failed!", 70 | error:{"msg":["Incorrected Login Information!"]} 71 | }); 72 | return; 73 | } 74 | }; 75 | 76 | 77 | exports.getDetail = async function setupUserFunction(req, res){ 78 | 79 | let userRecord = await userModel.getDetailById( 80 | req.userAccount.id 81 | ); 82 | 83 | res.status(200).send({ 84 | code:200, 85 | status: "success!", 86 | data:userRecord 87 | }); 88 | return; 89 | }; -------------------------------------------------------------------------------- /app/functions/middleware.function.js: -------------------------------------------------------------------------------- 1 | var jwt = require('jsonwebtoken'); 2 | var config = require('../lib/config'); 3 | 4 | function AdminAuthMiddleWare(req, res, next) { 5 | res.header("Access-Control-Allow-Origin", "http://localhost:3000"); // update to match the domain you will make the request from 6 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); 7 | var token = req.headers['x-access-token']; 8 | if (!token) 9 | return res.status(403).send({ code:403,status: 'failed!', error:[{"msg":'No token provided.'}] }); 10 | 11 | jwt.verify(token, config.Secret.admin, function(err, auth) { 12 | if (err) 13 | return res.status(403).send({ code:403,status: 'failed!', error:[{"msg":'Failed to authenticate token.'}] }); 14 | 15 | // if everything good, save to request for use in other routes 16 | //console.log(req.authType); 17 | if (auth.authType!='admin') 18 | return res.status(403).send({ code:403,status: 'failed!', error:[{"msg":'Failed to authenticate admin token.'}] }); 19 | req.authType = auth.authType; 20 | req.adminAccount = auth.adminAccount; 21 | console.log(auth.adminAccount); 22 | next(); 23 | }); 24 | } 25 | function UserAuthMiddleWare(req, res, next) { 26 | var token = req.headers['x-access-token']; 27 | if (!token) 28 | return res.status(403).send({ code:403,status: 'failed!', error:[{"msg":'No token provided.'}] }); 29 | console.log(config.Secret.user); 30 | jwt.verify(token, config.Secret.user, function(err, auth) { 31 | if (err) 32 | return res.status(403).send({ code:403,status: 'failed!', error:[{"msg":'Failed to authenticate token.'}] }); 33 | 34 | if (auth.authType!='user') 35 | return res.status(403).send({ code:403,status: 'failed!', error:[{"msg":'Failed to authenticate user token.'}] }); 36 | 37 | // if everything good, save to request for use in other routes 38 | req.authType = auth.authType; 39 | req.userAccount = auth.userAccount; 40 | next(); 41 | }); 42 | } 43 | 44 | module.exports = {AdminAuthMiddleWare,UserAuthMiddleWare}; -------------------------------------------------------------------------------- /app/lib/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | Database:{ 4 | HOST: process.env.DB_HOST, 5 | PORT: process.env.DB_PORT, 6 | USER: process.env.DB_USER, 7 | PASSWORD: process.env.DB_PASSWORD, 8 | DB: process.env.DB_NAME 9 | }, 10 | Secret:{ 11 | admin:"789hjkty5^&YHTYUJHT^&*IKHUIHT&*UJHT^&UJTYUIjt678ijntuif56yhgde45tfsw456yhtyUIKGT&", 12 | user:"kjy78ijgyuhg^&*U&*IKHU*IJY&*IJHG78ikjn8ikh8ijfe434567ujhgfdszxcvbnmkuytfdGU^%$EDFGH" 13 | }, 14 | Origin:{ 15 | allowFrom:process.env.ALLOW_FROM 16 | } 17 | 18 | }; -------------------------------------------------------------------------------- /app/models/admin.model.js: -------------------------------------------------------------------------------- 1 | const SqlAdapter = require("./sqlAdapter.js"); 2 | 3 | class Admin extends SqlAdapter{ 4 | 5 | constructor() { 6 | super(); 7 | this.tableName = "Admin"; 8 | } 9 | 10 | getByEmailAndPassword = async (email,password) => { 11 | // call the function of the parent class 12 | return this.getRecordByQuery(`SELECT * FROM ${this.tableName} WHERE email='${email}' AND password='${password}'`); 13 | 14 | }//end function 15 | 16 | async getDetailById(id){ 17 | // call the function of the parent class 18 | return this.getRecordByQuery(`SELECT * FROM ${this.tableName} WHERE id=${id}`); 19 | } // end async function 20 | 21 | }//end class 22 | 23 | module.exports = Admin; -------------------------------------------------------------------------------- /app/models/sqlAdapter.js: -------------------------------------------------------------------------------- 1 | const mysql = require("mysql"); 2 | const config = require("../lib/config.js"); 3 | // Create a connection to the database 4 | const mysqlConnection = mysql.createConnection({ 5 | host: config.Database.HOST, 6 | user: config.Database.USER, 7 | port: config.Database.PORT, 8 | password: config.Database.PASSWORD, 9 | database: config.Database.DB 10 | }); 11 | // open the MySQL connection 12 | mysqlConnection.connect(error => { 13 | if (error) throw error; 14 | console.log("Successfully connected to the database."); 15 | }); 16 | 17 | 18 | class SqlAdapter{ 19 | constructor() { 20 | this.sql = mysqlConnection; 21 | } 22 | 23 | insertData = (tableName,newRecord) => { 24 | 25 | return new Promise(resolve => { 26 | this.sql.query(`INSERT INTO ${tableName} SET ?`, newRecord, (err, res) => { 27 | if (err) { 28 | console.log("error: ", err); 29 | resolve({"isError":true,"data":err}); 30 | return; 31 | } 32 | resolve({"isError":false,"data":{ id: res.insertId, ...newRecord }}); 33 | }); 34 | });// end Promise 35 | }//end getRecordById function 36 | 37 | getRecordByQuery = (query) => { 38 | //console.log(query); 39 | return new Promise(resolve => { 40 | this.sql.query(query, (err, res) => { 41 | if (err) { 42 | resolve(null); 43 | return; 44 | } 45 | if (res.length) { 46 | resolve(res[0]); 47 | return; 48 | } 49 | else{ 50 | resolve(null); 51 | return; 52 | } 53 | });// end sql command 54 | });// end Promise 55 | }//end getRecordById function 56 | 57 | paginate = (query,row_count,page_at) => { 58 | page_at = page_at - 1; 59 | //row_count => total records per page. 60 | return new Promise(resolve => { 61 | let totalPage = 0; 62 | let startFrom = 0; 63 | let cmdString = `SELECT count(*) as totalCount from (${query}) totalCount;`; 64 | let paginatecmdString = ""; 65 | 66 | this.sql.query(cmdString, (err, res) => { 67 | if (err) { 68 | resolve(null); 69 | return; 70 | } 71 | if (res.length) { 72 | totalPage = Math.ceil(res[0].totalCount / row_count); 73 | startFrom = page_at * row_count; 74 | //generate pagination query 75 | paginatecmdString = query + ` LIMIT ${startFrom},${row_count}`; 76 | this.sql.query(paginatecmdString, (er, re) => { 77 | if (er) { 78 | resolve(null); 79 | return; 80 | } 81 | else{ 82 | resolve({ 83 | pagination:{ 84 | totalRecord:res[0].totalCount, 85 | count_per_page:row_count, 86 | totalPage:totalPage, 87 | current_records:re.length, 88 | page_at:page_at+1, 89 | }, 90 | data:re 91 | }); 92 | } 93 | });//end pagination data 94 | return; 95 | } 96 | else{ 97 | resolve(null); 98 | return; 99 | } 100 | });//end total record 101 | 102 | });// end Promise 103 | 104 | }//end paginate function 105 | 106 | }//end class 107 | 108 | module.exports = SqlAdapter; -------------------------------------------------------------------------------- /app/models/user.model.js: -------------------------------------------------------------------------------- 1 | const SqlAdapter = require("./sqlAdapter.js"); 2 | 3 | 4 | class User extends SqlAdapter{ 5 | constructor() { 6 | super(); 7 | this.tableName = "User"; 8 | } 9 | 10 | async create(newRecord){ 11 | // call the function of the parent class 12 | //newRecord.created_by = 8; 13 | return this.insertData(this.tableName,newRecord); 14 | } // end async function 15 | 16 | getByEmailAndPassword = async (email,password) => { 17 | // call the function of the parent class 18 | return this.getRecordByQuery(`SELECT * FROM ${this.tableName} WHERE email='${email}' AND password='${password}'`); 19 | 20 | }//end function 21 | 22 | async getDetailById(id){ 23 | // call the function of the parent class 24 | return this.getRecordByQuery(`SELECT * FROM ${this.tableName} WHERE id=${id}`); 25 | } // end async function 26 | 27 | }//end class 28 | 29 | module.exports = User; -------------------------------------------------------------------------------- /app/routes/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helloakn/api-authorization-nodejs-mysql/474f56f472b012cb26b3f289a79ceaef2baeb0d4/app/routes/.DS_Store -------------------------------------------------------------------------------- /app/routes/admin-api/admin.routes.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = (app,prefix) => { 3 | const AdminController = require("../../controllers/admin/admin.controller.js"); 4 | const {AdminAuthMiddleWare} = require("../../functions/middleware.function.js"); 5 | // http://localhost:3000/admin-api/admin/* -> 6 | app.prefix(`/${prefix}/admin`, function (adminRoute) { 7 | // http://localhost:3000/admin-api/admin/login -> 8 | adminRoute.route('/login').post(AdminController.login); 9 | adminRoute.route('/setup-user').post(AdminAuthMiddleWare,AdminController.setupUser); 10 | }); 11 | }; -------------------------------------------------------------------------------- /app/routes/index.routes.js: -------------------------------------------------------------------------------- 1 | module.exports = app => { 2 | let routePrefix = "admin-api"; 3 | //start Admin Routes 4 | // http://localhost:3000/admin-api/* -> 5 | require("./admin-api/admin.routes.js")(app,routePrefix); 6 | //end Admin Routes 7 | 8 | 9 | //start User Routes 10 | routePrefix = "user-api"; 11 | // http://localhost:3000/user-api/* -> 12 | require("./user-api/user.routes.js")(app,routePrefix); 13 | //end User Routes 14 | }; -------------------------------------------------------------------------------- /app/routes/user-api/user.routes.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = (app,prefix) => { 3 | const UserController = require("../../controllers/user/user.controller.js"); 4 | const {UserAuthMiddleWare} = require("../../functions/middleware.function.js"); 5 | // http://localhost:3000/admin-api/admin/* -> 6 | app.prefix(`/${prefix}/user`, function (userRoute) { 7 | // http://localhost:3000/user-api/user/login -> 8 | userRoute.route('/login').post(UserController.login); 9 | // http://localhost:3000/user-api/user/getdetail -> 10 | userRoute.route('/getdetail').post(UserAuthMiddleWare,UserController.getDetail); 11 | }); 12 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "back-office-backend-service", 3 | "version": "1.0.0", 4 | "description": "api for the admin backed service", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": " node app.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "cors": "^2.8.5", 14 | "crypto": "^1.0.1", 15 | "dotenv": "^16.0.0", 16 | "express": "^4.17.3", 17 | "jsonwebtoken": "^8.5.1", 18 | "mysql": "^2.18.1" 19 | } 20 | } 21 | --------------------------------------------------------------------------------