Express ~ JWT - rohit120582sharma/Documentation GitHub Wiki
- express - web framework
- mongoose - create models and schema
- config - Uued to retrieve jwtPrivateKey
- joi - validation function
- bcrypt - hashing the password to store in the database
- jsonwebtoken - generate and verify JWT
npm install --save express mongoose config joi jsonwebtoken bcrypt
-
Set environment variable in Mac:
export project_jwtPrivateKey=12345
-
In the
config/custom-environment-variables.json
, add the following:{ "jwtPrivateKey" : "project_jwtPrivateKey" }
We have defined a schema here that just has a name, email, password. We have also added a method to the schema to generate auth-token (JWT).
const config = require("config");
const mongoose = require('mongoose');
const Joi = require('joi');
const jwt = require('jsonwebtoken');
/**
* Creating a schema and model for a new user
* Adding a method to schema for generating an auth-token using JWT
*/
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true,
minlength: 3,
maxlength: 50
},
email: {
type: String,
required: true,
minlength: 5,
maxlength: 255,
unique: true
},
password: {
type: String,
required: true,
minlength: 3,
maxlength: 1024
},
isActive: {
type: Boolean,
required: true
},
isAdmin: Boolean, // give different access rights if admin or not
});
UserSchema.methods.generateAuthToken = function() {
// user's info to store in the token
const user = { _id: this._id, isAdmin: this.isAdmin };
// get the private key from the config file -> environment variable
const privateKey = config.get('jwtPrivateKey');
// generate a token
const token = jwt.sign(user, privateKey);
return token;
}
const User = mongoose.model('User', UserSchema);
/**
* Utilities
* validateUser() to check if all required fields are present before registering the user
* validateLoginUser() to check if all required fields are present before login the user
*/
function validateUser(user) {
const schema = {
name: Joi.string().min(3).max(50).required(),
email: Joi.string().min(5).max(255).required().email(),
password: Joi.string().min(3).max(255).required()
};
return Joi.validate(user, schema);
}
const validateLoginUser = (user) => {
const schema = {
name: Joi.string().min(3).max(50).required(),
password: Joi.string().min(3).max(255).required(),
};
return Joi.validate(schema, user);
}
exports.User = User;
exports.validateUser = validateUser;
exports.validateLoginUser = validateLoginUser;
This is our custom middleware that will be used to check if there is an existing and valid JWT present in the request headers. This is how we will be identifying the user.
const config = require("config");
const jwt = require("jsonwebtoken");
module.exports = function(req, res, next) {
// get the token from the header if present
const token = req.headers["x-access-token"] || req.headers["authorization"];
// if no token found, return response (without going to the next middelware)
if (!token) return res.status(401).send("Access denied. No token provided.");
// if token found, verify the token whether valid or not
try {
const decoded = jwt.verify(token, config.get("jwtPrivateKey"));
req.user = decoded;
next();
} catch (ex) {
res.status(401).send("Invalid token.");
}
};
Here we have defined two routes, one is to get the current user using the auth middleware we have created earlier. The second one is to register to the user. We will use bcrypt to hash the password.
const express = require("express");
const bcrypt = require("bcrypt");
const Joi = require('joi');
const router = express.Router();
const auth = require("../middleware/auth");
const { User, validateUser, validateLoginUser } = require("../models/user.model");
router.get("/me", auth, async (req, res) => {
const user = await User.findById(req.user._id).select("-password");
res.send(user);
});
router.post("/", async (req, res) => {
// validate the request body first
const { error } = validateUser(req.body);
if (error) return res.status(400).send(error.details[0].message);
// find an existing user
let user = await User.findOne({ email: req.body.email });
if (user) return res.status(400).send("User already registered.");
// if no record is available then create and save a new user
user = new User({
name: req.body.name,
password: req.body.password,
email: req.body.email,
isActive: true
});
user.password = await bcrypt.hash(user.password, 10);
await user.save();
// send response
res.send({
name: user.name,
email: user.email
});
});
router.post("/login", async (req, res) => {
// validate the request body first
const { error } = validateLoginUser(req.body);
if (error) return res.status(400).send(error.details[0].message);
// find an existing user
let user = await User.findOne({ email: req.body.email, isActive: true });
if (!user) return res.status(400).send("Invalid email or password.");
// validate password
const validPassword = await bcrypt.compare(req.body.password, user.password);
if (!validPassword) return res.status(400).send("Invalid email or password.");
// create token and send with response
const token = user.generateAuthToken();
res.header("x-access-token", token);
res.send({
_id: user._id,
name: user.name,
email: user.email
});
});
module.exports = router;
const config = require("config");
const express = require("express");
const mongoose = require("mongoose");
const app = express();
const usersRoute = require("./routes/user.route");
// use config module to get the privatekey, if no private key set, end the application
if (!config.get("jwtPrivateKey")) {
console.error("FATAL ERROR: jwtPrivateKey is not defined.");
process.exit(1);
}
// connect to MongoDB
function connectWithDatabase() {
mongoose
.connect("mongodb://localhost/nodejsauth", { useNewUrlParser: true })
.then(() => {
console.log("Connected to MongoDB...");
startTheServer();
})
.catch(err => {
console.error("FATAL ERROR: could not connect to MongoDB...");
process.exit(1);
});
}
// start the server
function startTheServer() {
// middlewares & route for api/users
app.use(express.json());
app.use("/api/users", usersRoute);
// start and listen the server
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}...`));
}
connectWithDatabase();