Node.js API Authentication with JWT (Json Web Token) - Auth Middleware

cyberwolves

CyberWolves

Posted on June 4, 2021

Node.js API Authentication with JWT (Json Web Token) - Auth Middleware

Hi, Today we are going to implement API authentication with JWT in node.js application. Authentication is most important feature in every application. Even if you are beginner feel free to try this tutorial, we gonna start from scratch. We also gonna write Auth middleware, which allow only authenticated people to access the route.

Authentication With JWT (Json Web Token) In React

For better understanding watch Demo Video

Source Code

Let's Start Coding...

App Overview :

Project Structure
Project_structure
Following table shows the overview of the Rest APIs that be exported :

Methods Urls Actions
POST /api/users Create user
POST /api/auth Authenticate user
GET /api/users/me Get authenticated user details

Create Node.js App and Install dependencies



$    mkdir node-auth-jwt
$    cd node-auth-jwt
$    npm init --yes
$    npm install express mongoose jsonwebtoken bcrypt joi dotenv


Enter fullscreen mode Exit fullscreen mode

express : Express is minimal and flexible Node.js web applicaton framework.
mongoose : Mongoose is an Object Data Modeling (ODM) library for MongoDB and Node.js.
jsonwebtoken : It's a compact URL of representing claims to be transferred between two parties.
bcrypt : It's a password hashing function.
joi : Joi is an object schema description language and validator for javascript objects.
dotenv : It loads environment variables from a .env file.

Setup Express Web Server
/index.js



require("dotenv").config();
const express = require("express");
const app = express();

app.use(express.json());

const port = process.env.PORT || 8080;
app.listen(port, () => console.log(`Listening on port ${port}...`));


Enter fullscreen mode Exit fullscreen mode

Configure Environmental Variables
/.env



DB = "mongodb://localhost/node-auth-api/"
JWTPRIVATEKEY = "secretkey"
SALT = 10


Enter fullscreen mode Exit fullscreen mode

Configure MongoDB Database
/db.js



const mongoose = require("mongoose");

module.exports = async () => {
    try {
        const connectionParams = {
            useNewUrlParser: true,
            useUnifiedTopology: true,
            useCreateIndex: true,
        };
        await mongoose.connect(process.env.DB, connectionParams);
        console.log("connected to database.");
    } catch (error) {
        console.log("could not connect to database", error);
    }
};


Enter fullscreen mode Exit fullscreen mode

Import db.js in index.js and call it



//...
const connection = require("./db");
const express = require("express");
const app = express();

connection();
app.use(express.json());
//...


Enter fullscreen mode Exit fullscreen mode

Create User Model
/models/user.js



const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const jwt = require("jsonwebtoken");
const Joi = require("joi");

const userSchema = new Schema({
    name: {
        type: String,
        required: true,
    },
    email: {
        type: String,
        required: true,
    },
    password: {
        type: String,
        required: true,
    },
});

userSchema.methods.generateAuthToken = function () {
    const token = jwt.sign({ _id: this._id }, process.env.JWTPRIVATEKEY);
    return token;
};

const User = mongoose.model("user", userSchema);

const validate = (user) => {
    const schema = Joi.object({
        name: Joi.string().required(),
        email: Joi.string().email().required(),
        password: Joi.string().required(),
    });
    return schema.validate(user);
};

module.exports = { User, validate };


Enter fullscreen mode Exit fullscreen mode

What we have done :

  • We have created user table with name, email and password.
  • With JWT, we generate token with payload of user id.
  • With Joi, we gonna validate data.

Register Route
/routes/users.js



const { User, validate } = require("../models/user");
const bcrypt = require("bcrypt");
const express = require("express");
const router = express.Router();

router.post("/", async (req, res) => {
    try {
        const { error } = validate(req.body);
        if (error) return res.status(400).send(error.details[0].message);

        const user = new User(req.body);

        const salt = await bcrypt.genSalt(Number(process.env.SALT));
        user.password = await bcrypt.hash(user.password, salt);
        await user.save();

        res.send(user);
    } catch (error) {
        console.log(error);
        res.send("An error occured");
    }
});

module.exports = router;


Enter fullscreen mode Exit fullscreen mode

Login Route
/routes/auth.js



const { User } = require("../models/user");
const bcrypt = require("bcrypt");
const Joi = require("joi");
const express = require("express");
const router = express.Router();

router.post("/", async (req, res) => {
    try {
        const { error } = validate(req.body);
        if (error) return res.status(400).send(error.details[0].message);

        const user = await User.findOne({ email: req.body.email });
        if (!user) return res.status(400).send("Invalid email or password");

        const validPassword = await bcrypt.compare(
            req.body.password,
            user.password
        );
        if (!validPassword)
            return res.status(400).send("Invalid email or password");

        const token = user.generateAuthToken();
        res.send(token);
    } catch (error) {
        console.log(error);
        res.send("An error occured");
    }
});

const validate = (user) => {
    const schema = Joi.object({
        email: Joi.string().email().required(),
        password: Joi.string().required(),
    });
    return schema.validate(user);
};

module.exports = router;


Enter fullscreen mode Exit fullscreen mode

Auth Middleware
/middleware/auth.js



const jwt = require("jsonwebtoken");

module.exports = (req, res, next) => {
    try {
        const token = req.header("x-auth-token");
        if (!token) return res.status(403).send("Access denied.");

        const decoded = jwt.verify(token, process.env.JWTPRIVATEKEY);
        req.user = decoded;
        next();
    } catch (error) {
        res.status(400).send("Invalid token");
    }
};


Enter fullscreen mode Exit fullscreen mode

User Get Route
/routes/users.js



const auth = require("../middleware/auth");
//...

router.get("/me", auth, async (req, res) => {
    try {
        const user = await User.findById(req.user._id).select("-password -__v");
        res.send(user);
    } catch (error) {
        console.log(error);
        res.send("An error occured");
    }
});

module.exports = router;


Enter fullscreen mode Exit fullscreen mode

Import Routes in Index.js



//...
const users = require("./routes/users");
const auth = require("./routes/auth");
//...

app.use(express.json());

app.use("/api/users", users);
app.use("/api/auth", auth);

//...


Enter fullscreen mode Exit fullscreen mode

That's it Run the server and test the APIs. If you found any mistakes or making code better please let me know in comment. I hope you have learned something.

Thank you...

💖 💪 🙅 🚩
cyberwolves
CyberWolves

Posted on June 4, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related