Authentication and Authorization with JWT in nodeJS

In the previous article we have discussed important points in authentication and authorization for more details you can visit this link and it’s recommended to read this article before starting the developing part Authentication & Authorization part one in this article we will build a simple application using nodejs and jwt .

Our application contains three end-points:

  • POST api/v1/login it’s a public route. User can login and enter his cerdentials.
  • GET api/v1/user get current session details (view my profile). It’s an authorized route (protected route) only the authorized users can access this api.
  • PATCH api/v1/user update my profile. It’s an authorized route (protected route) only the authorized users can access this api.

Requirements

  • Make sure that you have nodejs and npm installed in your system if not installed you can install from here NodeJS installation . To verify run the following command
    node -v This command display node installed version.
    npm -v This command display npm installed version.

verify nodejs success installation

  • Create a new folder for our project jwt .
  • Run the following command inside project directory to initiate node project. npm init after running this command package.json file created.

npm init

  • Install these packages:
    express which is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.

    body-parser middleware to parse the JSON body from the HTTP request.

    jsonwebtoken JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

    you can install these packages by running the following command npm i express body-parser jsonwebtoken after installing node_modules directory created and package.json will be updated also.

File & Project Strcuture

  • Create server.js our entry point.
  • Create .gitignore file specifies intentionally untracked files that Git should ignore.
  • Create src directory and inside this directroy create controllers , models , routes , middlewares , config folders.
  • Inside controllers create new files authentication.js for login logic and users.js for get profile details and updating account logic.
  • Inside routes create auth.js for authentication end-points login , users.js for user management end-points view & update profile, index.js for collecting all route files in single file.
  • Inside middlewares create auth.js this for authorization and protect our routes.
  • Inside models create users.js which contains all users in the system.
  • inside config create setting.json file which contains system variables and constants.
  • inside utils create api_response.js which returns an object for success and fail response based on data and err parameters passed to the function.

project structure

Development

models/users.js in this file we will create an array which contains users in the system i have created this array because we are not build a database application we foucs only on authentication and authorization using JWT and NodeJS .

const USERS = [
  {
    id: 1,
    firstName: "Mohamed",
    lastName: "Ali",
    username: "mohamed92",
    password: "1234",
    email: "mohamed.ali@gmail.com",
  },
  {
    id: 2,
    firstName: "Ahmed",
    lastName: "Ali",
    username: "ahmed93",
    password: "5678",
    email: "ahmed.ali@gmail.com",
  },
];
module.exports = USERS;

in config/setting.json

{
  "SECRET": "n7trywg653562hLKHJSHDB12zzzaertttygdCVB}{IK<>",
  "PORT": 5000,
  "EXPIRE": "1h"
}
  • SECRET should be a long random string it’s used for encrypting token and this key saved on the server and do not share this key with anyone.
  • PORT The Running port for our web application.
  • EXPIRE it’s one hour after one hour the generated token expires.

in utils/api_response.js

//This function returns response (success or err)
// Also returns network code 
module.exports = (data, code, err) => {
  let response = data
    ? { success: true, data, err: [] }
    : { success: false, data: [], err };
  return { response, code };
};

in controllers/authentication.js

"use strict";
const User = require("./../models/users");
const API_RESPONSE = require("./../utils/api_response");
const SETTING = require("./../config/setting.json");
const jwt = require("jsonwebtoken");
class Authentication {
  login(user) {
    let { username, password } = user;
    //validate that username and password having values
    let check = this.checkRequired({ username, password });
    if (check.length) {
      //return err msg in the response
      return API_RESPONSE(null, 400, check);
    } else {
      //check if user exist or not
      let userAccount = User.find((user) => {
        return user.username === username && user.password === password;
      });
      if (userAccount) {
        //return signed token expaires after one hour
        //and payload of {id,firstName,lastName}
        let payload = {
          id: userAccount.id,
          firstName: userAccount.firstName,
          lastName: userAccount.lastName,
        };
        const accessToken = jwt.sign(payload, SETTING.SECRET, {
          algorithm: "HS256",
          expiresIn: SETTING.EXPIRE,
        });
        return API_RESPONSE({ token: accessToken }, 200, null);
      } else {
        //return err msg check your username and password
        return API_RESPONSE(null, 400, [
          "Login fail check your username & password",
        ]);
      }
    }
  }
  checkRequired(fields) {
    const errors = [];
    let keys = Object.keys(fields);
    keys.forEach((key) => {
      if (!fields[key]) errors.push(`${key} is required!`);
    });
    return errors;
  }
}
module.exports = new Authentication();

now we need to handle our first end-point login inside routes/auth.js

"use strict";
const authenticationController = require("./../controllers/authentication");
module.exports = (app) => {
  app.post("/api/v1/login", (req, res, next) => {
    let { username, password } = req.body;
    let auth = authenticationController.login({ username, password });
    let { response, code } = auth;
    return res.status(code).json(response);
  });
};

in routes/index.js

"use strict";
module.exports = (app) => {
  require("./auth")(app);
};

Finally in our main entry point server.js

"use strict";
const express = require("express");
const bodyParser = require("body-parser");
const PORT = require("./src/config/setting.json").PORT || 4000;
const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
//our routes
require("./src/routes")(app);

app.listen(PORT, () => {
  console.log(`server running http://localhost:${PORT}`);
});

Testing & Results

Run this application by writing this command node server.js and by using postman you will find the follwoing results

Login without username and password

Login without sending username & password

Login with wrong username or password

Login with wrong username or password

Login with an existing account

Login with an existing account

Protecting Routes

Now we need to protect our routes so we will develop authentication middleware which handles this function which allow only the authorized users to access api/v1/user end-point.
in middlewares/auth.js

"use strict";
const SECRET = require("./../config/setting.json").SECRET;
const jwt = require("jsonwebtoken");
const API_RESPONSE = require("./../utils/api_response");
module.exports = (app) => {
  app.use((req, res, next) => {
    //read token from authorization header
    let authHeader = req.headers["authorization"];
    let token = authHeader && authHeader.split(" ")[1];
    //if header not contains token
    if (!token) {
      let apiRes = API_RESPONSE(null, 402, ["Un-Authorized !!"]);
      return res.status(apiRes.code).json(apiRes.response);
    }
    try {
      //decode payload using token and secret
      let decoded = jwt.verify(token, SECRET);
      req.user = decoded;
      next();
    } catch (err) {
      console.log(err);
      let apiRes = API_RESPONSE(null, 402, ["Un-Authorized !!"]);
      return res.status(apiRes.code).json(apiRes.response);
    }
  });
};

and in routes/users.js

"use strict";
const API_RESPONSE = require("./../utils/api_response");
module.exports = (app) => {
  app.get("/api/v1/user", (req, res, next) => {
    let user = req.user;
    let { code, response } = API_RESPONSE(user, 200, null);
    return res.status(code).json(response);
  });
};

Update the routes/index.js please notice the arrangement of calling routes. user routes called after auth middleware.

"use strict";
module.exports = (app) => {
  require("./auth")(app);
  require("./../middlewares/auth")(app);
  require("./users")(app);
};

Accessing user end-point without sending token

If you are trying to get user account details without login you are not allowed and get error message that you are not authorized.

Protected routes

Get User Details after login

Now if your logged in and trying to get your account details you are allowed to access this end-point and get account information.
first send the token in header the below image.

Accessing protected routes
After click on send you will get the following response (account details)

Protected routes

In this article, we have introduced you to JWT and how to implement JWT with Express. I hope that now you have a piece of good knowledge about how JWT works and how to implement it in your project.

As always the source code is available in GitHub