DEV Community

Cover image for Node.js API Authentication with JWT (Json Web Token) - Auth Middleware
Jahangeer
Jahangeer

Posted on • Updated on

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...

Discussion (4)

Collapse
developerphilo profile image
John Philip

Amazing article 👍. Spot on

Collapse
mengleangtuong profile image
Tuong Mengleang

Easy to understand for getting start node js express

Collapse
ahmedoooov profile image
Ahmedoooov

How do you redirect to login page in the authentication middleware when the token is no longer valid, I mean instead of just sending status code 403, I want to redirect the user to login page.

Collapse
filipemrf profile image
FMrF

Is this an advanced form? and can it be used in a large applications?