├── config ├── config.js └── config.SAMPLE.js ├── README.md ├── models └── user.js ├── server.js ├── package.json ├── .vscode └── launch.json ├── routes └── index.js ├── .gitignore ├── middleware └── auth.js └── controllers └── user.js /config/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | DB_HOST: "mongodb://uservishwas1:uservishwas1@ds149744.mlab.com:49744/eventsdb", 3 | TOKEN_SECRET: "secretKey", 4 | }; -------------------------------------------------------------------------------- /config/config.SAMPLE.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | DB_HOST: "DB_HOST", 3 | DB_USER: "DB_USER", 4 | DB_PASSWORD: "DB_PASSWORD", 5 | DB_NAME: "DB_NAME", 6 | TOKEN_SECRET: 'RANDOM_STRING' 7 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Authentication and Authorization Using JWT in Node JS with Express 2 | 3 | 4 | ## Project setup 5 | ``` 6 | npm install 7 | ``` 8 | 9 | ## Run Tests 10 | ``` 11 | npm start 12 | ``` 13 | -------------------------------------------------------------------------------- /models/user.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const Schema = mongoose.Schema; 4 | 5 | const userSchema = new Schema({ 6 | email: String, 7 | password: String, 8 | name:String, 9 | user_type_id:Number 10 | }); 11 | 12 | module.exports = mongoose.model('user', userSchema, 'users'); -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | 4 | app.use(express.json()); //or use body-parser middleware to parse the JSON body from HTTP request 5 | 6 | // Import Routes 7 | const authRoute = require('./routes/index'); 8 | 9 | 10 | // Route Middlewares 11 | app.use('/api', authRoute); 12 | 13 | 14 | const port = 3000; 15 | app.listen(port, function(){ 16 | console.log("Server running on localhost:" + port); 17 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "keywords": [], 11 | "author": "Mo lotfy", 12 | "license": "ISC", 13 | "dependencies": { 14 | "bcryptjs": "^2.4.3", 15 | "body-parser": "^1.18.2", 16 | "cors": "^2.8.4", 17 | "express": "^4.17.1", 18 | "jsonwebtoken": "^8.1.1", 19 | "mongoose": "^5.0.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "program": "${workspaceFolder}\\server.js" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | const router = require('express').Router(); 2 | const { verifyUserToken, IsAdmin, IsUser } = require("../middleware/auth"); 3 | const userController = require('../controllers/user'); 4 | 5 | // Register a new User 6 | router.post('/register', userController.register); 7 | 8 | // Login 9 | router.post('/login', userController.login); 10 | 11 | // Auth user only 12 | router.get('/events', verifyUserToken, IsUser, userController.userEvent); 13 | 14 | // Auth Admin only 15 | router.get('/special', verifyUserToken, IsAdmin, userController.adminEvent); 16 | 17 | module.exports = router; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /dist-server 6 | /tmp 7 | /out-tsc 8 | 9 | # dependencies 10 | /node_modules 11 | 12 | # IDEs and editors 13 | /.idea 14 | .project 15 | .classpath 16 | .c9/ 17 | *.launch 18 | .settings/ 19 | *.sublime-workspace 20 | 21 | # IDE - VSCode 22 | .vscode/* 23 | !.vscode/settings.json 24 | !.vscode/tasks.json 25 | !.vscode/launch.json 26 | !.vscode/extensions.json 27 | 28 | # misc 29 | /.sass-cache 30 | /connect.lock 31 | /coverage 32 | /libpeerconnection.log 33 | npm-debug.log 34 | testem.log 35 | /typings 36 | 37 | # e2e 38 | /e2e/*.js 39 | /e2e/*.map 40 | 41 | # System Files 42 | .DS_Store 43 | Thumbs.db 44 | -------------------------------------------------------------------------------- /middleware/auth.js: -------------------------------------------------------------------------------- 1 | const config = require("../config/config"); 2 | const jwt = require('jsonwebtoken') 3 | 4 | exports.verifyUserToken = (req, res, next) => { 5 | let token = req.headers.authorization; 6 | if (!token) return res.status(401).send("Access Denied / Unauthorized request"); 7 | 8 | try { 9 | token = token.split(' ')[1] // Remove Bearer from string 10 | 11 | if (token === 'null' || !token) return res.status(401).send('Unauthorized request'); 12 | 13 | let verifiedUser = jwt.verify(token, config.TOKEN_SECRET); // config.TOKEN_SECRET => 'secretKey' 14 | if (!verifiedUser) return res.status(401).send('Unauthorized request') 15 | 16 | req.user = verifiedUser; // user_id & user_type_id 17 | next(); 18 | 19 | } catch (error) { 20 | res.status(400).send("Invalid Token"); 21 | } 22 | 23 | } 24 | exports.IsUser = async (req, res, next) => { 25 | if (req.user.user_type_id === 0) { 26 | next(); 27 | } 28 | return res.status(401).send("Unauthorized!"); 29 | } 30 | exports.IsAdmin = async (req, res, next) => { 31 | if (req.user.user_type_id === 1) { 32 | next(); 33 | } 34 | return res.status(401).send("Unauthorized!"); 35 | 36 | } -------------------------------------------------------------------------------- /controllers/user.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const config = require("../config/config"); 3 | const bcrypt = require("bcryptjs"); 4 | const jwt = require("jsonwebtoken"); 5 | const User = require("../models/user.js"); 6 | 7 | // Connect to DB 8 | const db = config.DB_HOST; 9 | mongoose.connect(db, function (err) { 10 | if (err) { 11 | console.error('Error! ' + err) 12 | } else { 13 | console.log('Connected to mongodb') 14 | } 15 | }); 16 | 17 | exports.register = async (req, res) => { 18 | 19 | //Hash password 20 | const salt = await bcrypt.genSalt(10); 21 | const hasPassword = await bcrypt.hash(req.body.password, salt); 22 | 23 | // Create an user object 24 | let user = new User({ 25 | email: req.body.email, 26 | name: req.body.name, 27 | password: hasPassword, 28 | user_type_id: req.body.user_type_id 29 | }) 30 | 31 | // Save User in the database 32 | user.save((err, registeredUser) => { 33 | if (err) { 34 | console.log(err) 35 | } else { 36 | // create payload then Generate an access token 37 | let payload = { id: registeredUser._id, user_type_id: req.body.user_type_id || 0 }; 38 | const token = jwt.sign(payload, config.TOKEN_SECRET); 39 | 40 | res.status(200).send({ token }) 41 | } 42 | }) 43 | } 44 | 45 | exports.login = async (req, res) => { 46 | 47 | User.findOne({ email: req.body.email }, async (err, user) => { 48 | if (err) { 49 | console.log(err) 50 | } else { 51 | if (user) { 52 | const validPass = await bcrypt.compare(req.body.password, user.password); 53 | if (!validPass) return res.status(401).send("Mobile/Email or Password is wrong"); 54 | 55 | // Create and assign token 56 | let payload = { id: user._id, user_type_id: user.user_type_id }; 57 | const token = jwt.sign(payload, config.TOKEN_SECRET); 58 | 59 | res.status(200).header("auth-token", token).send({ "token": token }); 60 | } 61 | else { 62 | res.status(401).send('Invalid mobile') 63 | } 64 | 65 | } 66 | }) 67 | } 68 | 69 | // Access auth users only 70 | exports.userEvent = (req, res) => { 71 | let events = [ 72 | { 73 | "_id": "1", 74 | "name": "Auto Expo", 75 | "description": "lorem ipsum", 76 | "date": "2012-04-23T18:25:43.511Z" 77 | }, 78 | { 79 | "_id": "2", 80 | "name": "Auto Expo", 81 | "description": "lorem ipsum", 82 | "date": "2012-04-23T18:25:43.511Z" 83 | }, 84 | { 85 | "_id": "3", 86 | "name": "Auto Expo", 87 | "description": "lorem ipsum", 88 | "date": "2012-04-23T18:25:43.511Z" 89 | } 90 | ] 91 | res.json(events) 92 | }; 93 | 94 | exports.adminEvent = (req, res) => { 95 | let specialEvents = [ 96 | { 97 | "_id": "1", 98 | "name": "Auto Expo Special", 99 | "description": "lorem ipsum", 100 | "date": "2012-04-23T18:25:43.511Z" 101 | }, 102 | { 103 | "_id": "2", 104 | "name": "Auto Expo Special", 105 | "description": "lorem ipsum", 106 | "date": "2012-04-23T18:25:43.511Z" 107 | }, 108 | { 109 | "_id": "3", 110 | "name": "Auto Expo Special", 111 | "description": "lorem ipsum", 112 | "date": "2012-04-23T18:25:43.511Z" 113 | }, 114 | { 115 | "_id": "4", 116 | "name": "Auto Expo Special", 117 | "description": "lorem ipsum", 118 | "date": "2012-04-23T18:25:43.511Z" 119 | }, 120 | { 121 | "_id": "5", 122 | "name": "Auto Expo Special", 123 | "description": "lorem ipsum", 124 | "date": "2012-04-23T18:25:43.511Z" 125 | }, 126 | { 127 | "_id": "6", 128 | "name": "Auto Expo Special", 129 | "description": "lorem ipsum", 130 | "date": "2012-04-23T18:25:43.511Z" 131 | } 132 | ] 133 | res.json(specialEvents) 134 | 135 | } --------------------------------------------------------------------------------