Express ~ JWT - rohit120582sharma/Documentation GitHub Wiki

Setup

Install the following dependencies:

  • 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

Config environment variable

  • Set environment variable in Mac:

    export project_jwtPrivateKey=12345
  • In the config/custom-environment-variables.json, add the following:

    {
        "jwtPrivateKey" : "project_jwtPrivateKey"
    }


Creating User Model

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;

Authentication Middleware

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.");
    }
};

Handling User Routes

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;

Entrance to the app — index.js

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();

⚠️ **GitHub.com Fallback** ⚠️