DEV Community

Cover image for User Authentication with JWT tokens in node js
AMANn5153
AMANn5153

Posted on

User Authentication with JWT tokens in node js

User authentication is the most important part of developing a website. To prevent any malicious activity in user accounts, we must implement a strong authentication system. Today, we will explore user authentication using JSON web tokens.



Getting Started

first, we need to set up our Node.js boilerplate using Express. Make sure you have installed the npm and node

create a directory for your project. We are going to use server

go inside the newly created directory and run

npm init

This will initialize our project with a package.json file.

Now we will need to install Express

npm install express

create a new file index.js.

put following code in the file


const express = require('express')
const app = express()
require("./db/conn")

const PORT=8083

app.use(require("./route/router"))

app.listen(PORT, () => console.log(`connected to port number ${PORT}`))
Enter fullscreen mode Exit fullscreen mode

now we will need another dependency "mongoose"

npm install mongoose


Database Connection

first we are going to connect our database.

create a new folder as db and create a file inside it as conn.js

paste the following code inside the file

const mongoose=require("mongoose")

const DB=process.env.DATABASE;

mongoose.connect(DB)
.then(()=>{
console.log("connection successfull")
})
.catch((e)=>{console.log(e)})
Enter fullscreen mode Exit fullscreen mode

In this code snippet, we are connecting our database to MongoDB Atlas by importing Mongoose. We access the database connection link from the config.env file using the process.env.DATABASE variable. The config.env file stores custom environment variables, which can be created using the dotenv library


Creating User Collection

lets create collection in database with mongoose schema

first create new folder with name models.

create a new file user.model.js inside the folder

user.model.js will have our user data such as name, email,password.

paste the following code in the user.model.js

const mongoose=require("mongoose");

const user=new mongoose.Schema({
name:{
type:String
},
email:{
type:String
},
password:{
type:String
},
token:{
type:String
}
)

const User=new mongoose.model("User",user)

module.exports=User
Enter fullscreen mode Exit fullscreen mode

we are creating schema here for the collection.
Mongoose Schema defines document's properties, default values, types of data, validators, etc. In contrast, a Mongoose model provides an interface for the database to create, query, update, delete records, and so on.

mongoose model accepts two parameters
mongoose.model(collection name,collection schema)


Installing JWT

before moving ahead lets just install the JWT package

npm i jsonwebtoken


Creating Routes and Authentication for User

After creating collection now lets create routes.

create a new folder "routes" in this folder create router.js file

paste the following code into the file

const express=require("express")
const router=express.Router()
const mongoose=require("mongoose")
const User=require("../model/user.model.js")

router.post("/Login",async (req,res)=>{       //Login  API

  const {email,Password}=req.body;

  if(!email || !Password){
   return res.status(403).json({error:"empty Fields"})
  }

  try{
    const exist=await User.findOne({email})
    if(exist){
      if(exist.password==Password){
        const token= await exist.generateAuthToken();
        res.cookie("authcookie",token,{
          expires:new Date(Date.now()+36000000),
          httpOnly:false,
        }) 
        res.status(200).json({token:token})                                                                                        
      }
      else{
        return res.status(401).json({error:"invalid credentials"})
      }
    }
    else{
      return res.status(401).json({error:"invalid credentials"})
}}catch(e){
  console.log(e)
  res.status(500).json({error:"wont be able to login"})
}}
)
Enter fullscreen mode Exit fullscreen mode

here we are checking if email and password are not empty if empty we are returning status code 403 "Empty fields"

If the password and email fields are not empty, we check if the email exists in the collection. If it does, we compare the entered password with the stored password to determine if they match. If the passwords match, we call the generateAuthToken() method to generate an authentication token.

now go to user.model.js and paste this code

user.methods.generateAuthToken=async function(){
    try{
    const tokenGen= jwt.sign({_id:this._id},process.env.SECRET)//genertaes token
    this.tokens=this.tokens.splice(0,1,{token:tokenGen})
     await this.save();
     return tokenGen;
    }
     catch(e){
        console.log(e)
     }
}
Enter fullscreen mode Exit fullscreen mode

generateAuthToken function generates the token .

jwt.sign() sign takes two parameter id and JWT_secret and returns signed token (it is recommended that JWT_secret must be stored in config.env file).


Creating Middleware

create a new folder middleware inside the folder create a new file authentication.js

paste the following code :-

const jwt=require("jsonwebtoken")
const User=require("../models/user.model.js")


const authenticate= async (req,res,next)=>{
    try{
    const token= req.cookies.authcookie || req.headers["x-access-token"];  // taking token

    const authnToken= jwt.verify(token,process.env.SECRET)//verfify token with secret key{token is made up of user unique id and secret key} return unique id

 const userInfo= await User.find({_id:authnToken._id},{"tokens.token":token})//finding document that matches the unique id and token 

    if(!userInfo){res.status(209).json({error:"user info is not available"})}
        req.token=token;
        req.userinfo=userInfo;
        req.userId=userInfo[0]._id;
        next();
    }
    catch(e){
        res.status(401).json({message:"Please loggin first"})
        console.log(e)
    }

}

module.exports=authenticate


Enter fullscreen mode Exit fullscreen mode

The jwt.verify function takes a token and a secret key (which is composed of the user's unique ID) as parameters. It returns the user's unique ID.

Now let’s create the /somepage route and update router.js with the following code to test the middleware

router.get("/somepage",authetication,(req,res)=>{
console.log("working")
})
Enter fullscreen mode Exit fullscreen mode

Checking The API In POSTMAN

paste the localhost:8003/login into the path

pass email and Password in body as a JSON

Image description

it gives token as response

for now copy this token

Change the route to /somepage and include the x-access-token key in the headers with the token value pasted as its value. Then, send the request

Image description

token is verified by middleware.


Conclusion

In this tutorial we learned about JWT, authentication, authorization and how to develop an API using JWT token for authentication in Node.js.

Top comments (2)

Collapse
 
adderek profile image
Maciej Wakuła

Good start but you should now read about the header and possible encryption methods - especially "none" as your code likely would allow such non-signed token.
You should probably use private/public key, cache public key and assume that it is changing often. RSA encryption is not considered quantum-safe so soon other methods would be required. I am not sure if it was jsonwebtoken library that had remote call exploit fixed recently. And if it was calling fetch for certificate on every .verify call. DB access could be improved. You should check if errors thrown by the library (especially any promise that is created and not fulfilled while response is already returned) are properly handled. Many traps in there :)
Overall a nice and professional article - good job :)

Collapse
 
mezieb profile image
Okoro chimezie bright

Nice work thanks for sharing👍