How to Build an Authentication API with JWT Token in Node.js 

Spread the love

The main focus of this tutorial is to demonstrate the utilization of JWT in Node.js for endpoint security and user authentication. 

 

While writing code and creating applications may be relatively straightforward, the challenge often lies in managing authentication and authorization.

 

What is authentication and authorization?

 

Authentication and authorization are two important concepts in computer security that are used to control access to resources and protect sensitive information.

 

Authentication

Authentication is the process of verifying the identity of a user or system by requiring some form of credentials, such as a username and password, a security token, or a biometric factor like a fingerprint or facial recognition. This process ensures that only authorized individuals or systems are granted access to sensitive resources or information.

 

Authorization

Authorization, on the other hand, is the process of determining what actions a user or system is permitted to perform once they have been authenticated. This is done by defining a set of permissions or access rights that specify which resources or information a user can access and what actions they are allowed to perform on those resources. Authorization is typically enforced through access control mechanisms like role-based access control (RBAC), where users are assigned roles that define their level of access to different resources based on their job responsibilities and permissions.

 

In summary, authentication is the process of verifying the identity of a user or system, while authorization is the process of determining what actions that user or system is allowed to perform once their identity has been verified. Both are important for maintaining the security of computer systems and protecting sensitive information from unauthorized access.

 

What is JWT

 

JWT stands for JSON Web Token, which is a compact, URL-safe means of representing claims to be transferred between two parties. It is a self-contained token that contains a set of claims, such as the identity of the user or their authorization level, encoded in a JSON object. The JWT is digitally signed to ensure the authenticity and integrity of the claims it carries.

 

JWTs are commonly used for authentication and authorization purposes in web applications and APIs. They allow servers to verify the identity of a user without requiring the user to repeatedly authenticate themselves, making them useful for stateless and distributed systems.

 

JWTs are typically composed of three parts separated by dots: the header, the payload, and the signature. The header contains information about the type of token and the algorithm used to sign it. The payload contains the claims or information being transferred. The signature is used to verify that the token was not tampered with during transmission.

 

Building an API using JWT token for authentication in Node.js

 

To build an authentication API with JWT tokens in Node.js, you can follow these steps:

  • Create a new Node.js project and initialize it with npm.

 

mkdir auth-api

cd auth-api

npm init -y

 

  1. Create files and directories

Firstly, we executed the command “npm init -y” to initialize npm and create a package.json file in step 1. To proceed further, we have to create directories and files such as model, middleware, and config, including user.js, auth.js, and database.js. The following commands can be used for this purpose.

 

mkdir model middleware config

touch config/database.js middleware/auth.js model/user.js

 

We can now create the index.js and app.js files in the root directory of our project with the command.

 

touch app.js index.js

 

  1. Install dependencies

We’ll install several dependencies like mongoose, jsonwebtoken, express dotenv bcryptjs. Additionally, we’ll install nodemon as a development dependency, which will automatically restart the server upon detecting any changes made. In this tutorial, we’ll be utilizing MongoDB, which is why we’ll install mongoose.

 

It’s important to note that the authentication process will not be limited to the database used in this article, as we will be validating user credentials against our database.

 

npm install mongoose express jsonwebtoken dotenv bcryptjs

 

npm install nodemon -D

 

  1. Create a Node.js server and connect your database

Let’s begin by setting up our Node.js server and establishing a connection to the database. To do this, add the code snippets provided below in the following order: app.js, index.js, database.js, and .env files

 

In  database.js.

 

config/database.js:

 

const mongoose = require(“mongoose”);

 

const { MONGO_URI } = process.env;

 

exports.connect = () => {

 

 // Connecting to the database

 

 mongoose

 

   .connect(MONGO_URI, {

 

     useNewUrlParser: true,

 

     useUnifiedTopology: true,

 

     useCreateIndex: true,

 

     useFindAndModify: false,

 

   })

 

   .then(() => {

 

     console.log(“Successfully connected to database”);

 

   })

 

   .catch((error) => {

 

     console.log(“database connection failed. exiting now…”);

 

     console.error(error);

 

     process.exit(1);

 

   });

 

};

 

In app.js:

jwt-project/app.js

 

require(“dotenv”).config();

 

require(“./config/database”).connect();

 

const express = require(“express”);

 

const app = express();

 

app.use(express.json());

 

// Logic goes here

 

module.exports = app;

 

In index.js:

jwt-project/index.js

 

const http = require(“http”);

 

const app = require(“./app”);

 

const server = http.createServer(app);

 

const { API_PORT } = process.env;

 

const port = process.env.PORT || API_PORT;

 

// server listening 

 

server.listen(port, () => {

 

 console.log(`Server running on port ${port}`);

 

});

 

It appears that our file requires certain environment variables. In case you haven’t created one yet, you can make a new .env file and include the necessary variables before initiating our application.

 

In .env.

 

API_PORT=4001

 

MONGO_URI= //Your database URI here

 

To start the server, modify the scripts object in our package.json file to resemble the one shown below.

 

“scripts”: {

 

   “start”: “node index.js”,

 

   “dev”: “nodemon index.js”,

 

   “test”: “echo \”Error: no test specified\” && exit 1″

 

 }

 

After inserting the code snippet into app.js, index.js, and database.js, we established our node.js server in index.js and included the app.js file with preconfigured routes. Additionally, we utilized mongoose to establish a connection to our database, as mentioned in database.js.

 

To ensure that both the server and the database are operational without any issues, run the command “npm run dev“.

 

  1. Create user model and route

The schema for user details will be established during the initial sign-up process, and when a user logs in, their credentials will be verified against the saved information.

 

 To implement this, you can insert the code snippet provided below into the user.js file located in the model folder.

 

model/user.js

 

const mongoose = require(“mongoose”);

const userSchema = new mongoose.Schema({

 first_name: { type: String, default: null },

 last_name: { type: String, default: null },

 email: { type: String, unique: true },

 password: { type: String },

 token: { type: String },

 

});

 

module.exports = mongoose.model(“user”, userSchema);

 

Let us now create the routes for the registration and login functionalities. To achieve this, you can insert the code snippets below into the app.js file located in the root directory.

 

app.js

 

// importing user context

 

const User = require(“./model/user”);

 

// Register

 

app.post(“/register”, (req, res) => {

 

// our register logic goes here…

 

});

 

// Login

 

app.post(“/login”, (req, res) => {

 

// our login logic goes here

 

});

  1. Implement register and login functionality

We’ll be implementing these two routes in our application.To ensure security, we will be utilizing JWT to sign the credentials and bcrypt to encrypt the password prior to storing them in the database.

On the /register route, we will perform the following actions:

  • Retrieve user input
  • Validate the user input
  • Verify if the user already exists
  • Encrypt the user’s password
  • Add the user to our database
  • Generate a signed JWT token

To update the structure of the /register route that was previously established, we will make the necessary modifications as shown below.

 

app.js

 

// …

 

app.post(“/register”, async (req, res) => {

 // Our register logic starts here

 try {

   // Get user input

   const { first_name, last_name, email, password } = req.body;

 

   // Validate user input

 

   if (!(email && password && first_name && last_name)) {

 

     res.status(400).send(“All input is required”);

 

   }

 

   // check if user already exist

 

   // Validate if user exist in our database

 

   const oldUser = await User.findOne({ email });

   if (oldUser) {

     return res.status(409).send(“User Already Exist. Please Login”);

 

   }

   //Encrypt user password

 

   encryptedPassword = await bcrypt.hash(password, 10);

 

   // Create user in our database

 

   const user = await User.create({

 

     first_name,

 

     last_name,

 

     email: email.toLowerCase(), // sanitize: convert email to lowercase

 

     password: encryptedPassword,

 

   });

 

   // Create token

 

   const token = jwt.sign(

 

     { user_id: user._id, email },

 

     process.env.TOKEN_KEY,

 

     {

 

       expiresIn: “2h”,

 

     }

 

   );

 

   // save user token

 

   user.token = token;

 

   // return new user

 

   res.status(201).json(user);

 

 } catch (err) {

 

   console.log(err);

 

 }

 

 // Our register logic ends here

 

});

 

// …

 

Note:Update your .env file with a TOKEN_KEY, which can be a random string.

 

After a successful registration, the response displayed below will be obtained by using Postman for testing the endpoint.

 

For the /login route, we will:

 

  • Get user input.
  • Validate user input.
  • Validate if the user exists.
  • Verify user password against the password we saved earlier in our database.
  • And finally, create a signed JWT token.

 

Modify the /login route structure we created earlier to look like shown below.

 

// …

app.post(“/login”, async (req, res) => {

 

 // Our login logic starts here

 try {

 

   // Get user input

 

   const { email, password } = req.body;

 

   // Validate user input

 

   if (!(email && password)) {

 

     res.status(400).send(“All input is required”);

 

   }

 

   // Validate if user exist in our database

 

   const user = await User.findOne({ email });

 

   if (user && (await bcrypt.compare(password, user.password))) {

     // Create token

 

     const token = jwt.sign(

 

       { user_id: user._id, email },

 

       process.env.TOKEN_KEY,

 

       {

         expiresIn: “2h”,

       }

 

     );

 

     // save user token

 

     user.token = token;

 

     // user

 

     res.status(200).json(user);

 

   }

   res.status(400).send(“Invalid Credentials”);

 

 } catch (err) {

 

   console.log(err);

 }

 

 // Our register logic ends here

 

});

 

// …

After successfully logging in, the response displayed below will be obtained by using Postman for testing purposes.

 

  1. Create middleware for authentication

 

After successfully creating and logging in a user, we will proceed to create a route that necessitates the presence of a user token in the header. This token is the JWT token that was generated previously.

 

Please insert the given code snippet into auth.js.

 

middleware/auth.js

 

const jwt = require(“jsonwebtoken”);

 

const config = process.env;

 

const verifyToken = (req, res, next) => {

 

 const token =

 

   req.body.token || req.query.token || req.headers[“x-access-token”];

 

 if (!token) {

 

   return res.status(403).send(“A token is required for authentication”);

 

 }

 

 try {

 

   const decoded = jwt.verify(token, config.TOKEN_KEY);

 

   req.user = decoded;

 

 } catch (err) {

 

   return res.status(401).send(“Invalid Token”);

 

 }

 

 return next();

 

};

 

module.exports = verifyToken;

 

Now let’s create the /welcome route and update app.js with the following snippet to test the middleware.

 

App.js

 

const auth = require(“./middleware/auth”);

app.post(“/welcome”, auth, (req, res) => {

 res.status(200).send(“Welcome 🙌 “);

 

});

 

If we attempt to access the /welcome route without providing a token in the header using the x-access-token key, you can see the result below.

 

We can now add a token in the header with the key x-access-token and re-test.

See the image below for the response.

 

Conclusion

Through this tutorial, we gained knowledge about JWT, the concepts of authentication and authorization, and the process of building an API utilizing JWT tokens for authentication in Node.js.

admin

admin

Leave a Reply

Your email address will not be published. Required fields are marked *