JWT authentication in nodejs with example

In this tutorial, we will be looking at how to add JWT authentication in a nodejs application.

By definition JSON Web Tokens are an open, industry-standard RFC 7519 method for representing claims securely between two parties.

So let’s not waste any more time and get started.

Agenda

The agenda for this tutorial will be to create a basic register/login API and using JWT authentication to protect/guard specific routes in our nodejs API. And for that, we will be creating 3 routes register, login, and get user.

Initialize our project and install dependencies

First of all let’s create a new folder and open a terminal in our favourite code editor

npm init -y
npm install express dotenv http-errors jsonwebtoken lodash simple-json-db uuid

Set environment variables

After that let’s create a “.env” file inside our project’s root directory and paste it the below values as per your choice

JWT_ACCESS_TOKEN_SECRET=”YOUR_SECRET_OF_YOUR_CHOICE”

JWT_EXPIRY=”EXPRY_TIME_ACCORDINGLY”

JSON_DB_PATH=./db.json

# this secret will be used to create our token
JWT_ACCESS_TOKEN_SECRET=kjh7jg6$hgjh
# time after which our token will expire, 1m is 1 minute.
JWT_EXPIRY=1m
# path for our json database(dummy database)
JSON_DB_PATH=./db.json

Setup directory structure

Now we have created 4 directories for different purposes.

  • Controllers directory: contains all our controller files
  • Middleware directory: contains all our middlewares
  • Utils directory: contains our utilities
  • Routes directory: contains all our routes

Create our app’s entry file

Now that we are ready with the directories, we are good to go.

Firstly, create an app.js file inside our root directory.

This will be our app’s entry file. As we can see we have imported some routes which we will see later in the tutorial. But first, copy-paste the below code into our app.js

require('dotenv').config()
const express = require('express')

const app = express()

const authRoutes = require('./routes/auth')
const userRoutes = require('./routes/user')

// middlewares

app.use(express.json())

app.use('/', authRoutes)
app.use('/user', userRoutes)

// error handling
app.use((err, req, res, next) => {
  res.status(err.status || 500).send({
    error: {
      status: err.status || 500,
      message: err.message
    }
  })
})
app.listen(process.env.PORT || 3000, () => {
  console.log('server is running', process.env.PORT || 3000)
})

process.on('SIGINT', async () => {
  process.exit(1)
})

Create our controllers

After that, we will be creating our controllers.

Inside our controller directory, we will be creating 2 controller files auth.js and user.js

auth.js: contains logic for our register and login functionality.

const { v4: uuid } = require('uuid')
const creatError = require('http-errors')
const JSONdb = require('simple-json-db')
const db = new JSONdb(process.env.JSON_DB_PATH, { asyncWrite: true })
const { signJwtToken } = require('../utils/jwt')
/**
 * Access token delivery handler
 */
const tokenHandler = async (user) => {
  try {
    // generate token
    const accessToken = await signJwtToken(user, {
      secret: process.env.JWT_ACCESS_TOKEN_SECRET,
      expiresIn: process.env.JWT_EXPIRY
    })
    return Promise.resolve(accessToken)
  } catch (error) {
    return Promise.reject(error)
  }
}

// handles register
exports.register = async (req, res, next) => {
  try {
    const { email, password } = req.body
    // this is just a demo code and not for production
    db.set(email, JSON.stringify({ id: uuid(), username: `Demo username ${uuid()}`, password }))
    res.status(201)
    res.send('Account created successfully')
  } catch (error) {
    next(error)
  }
}

// handles login
exports.login = async (req, res, next) => {
  try {
    const { email, password } = req.body

    const userData = db.get(email)

    if (!userData) throw creatError.NotFound()
    
    const { id, username, password: dbPassword } = JSON.parse(userData)

    if (!(id && (password === dbPassword))) throw creatError.Unauthorized()
    
    const token = await tokenHandler({ id, username, email })
    res.send(token)
  } catch (error) {
    next(error)
  }
}

Remember we are storing the user registration data in a JSON database and so we will also need some way to retrieve it. So let’s create our user.js controller file.

user.js: contains logic for retrieving users from our JSON database.

const creatError = require('http-errors')
const JSONdb = require('simple-json-db')
const db = new JSONdb(process.env.JSON_DB_PATH, { asyncWrite: true })
exports.getUser = async (req, res, next) => {
  try {
    // checking for any error occurance
    const { email } = req.payload
    if (!email) throw creatError.Unauthorized()
    
    const userData = db.get(email)

    if (!userData) throw creatError.NotFound()

    // creating user as json
    const userDataObj = JSON.parse(userData)

    // remove the password key before sending it to client
    delete userDataObj.password

    res.status(200).send(userDataObj)
  } catch (error) {
    next(error)
  }
}

Create utitilies required for JWT authentication in nodejs

Once we are done with our controllers we need to create a jwt.js file inside our utils directory which will hold the functionality of signing and verifying our JWT tokens.

const jwt = require('jsonwebtoken')
const creatError = require('http-errors')
module.exports = {
  signJwtToken: (data, { secret, expiresIn }) => {
    return new Promise((resolve, reject) => {
      const options = {
        expiresIn
      }
      jwt.sign(data, secret, options, (err, token) => {
        if (err) return reject(creatError.InternalServerError())
        resolve(token)
      })
    })
  },
  // verify tokens
  verifyJwtToken: ({ token, secret }) => {
    return new Promise((resolve, reject) => {
      jwt.verify(token, secret, (err, payload) => {
        if (err) {
          const message = err.name === 'TokenExpiredError' ? err.message : 'Unauthorized'
          return reject(creatError.NotAcceptable(message))
        }
        resolve(payload)
      })
    })
  }
}

Create middlewares

Next, we need a middleware that will check if the user requesting a protected route has a valid JWT token.

So let’s create a file named auth.js in our middleware directory and within that, we will have a function named accessTokenValidator which as the name suggests checks for token validity and return an error in case the token is not a valid one.

require('dotenv').config()

const _getKeyValue = require('lodash/get')
const { verifyJwtToken } = require('../utils/jwt')

module.exports = {
  accessTokenValidator: async (req, res, next) => {
    try {
      let token = null
      token = _getKeyValue(req.headers, 'authorization', null)

      if (!token) throw creatError.Unauthorized()
      token = token.split(' ')[1]
      req.payload = await verifyJwtToken({ token, secret: process.env.JWT_ACCESS_TOKEN_SECRET })
      next()
    } catch (error) {
      next(error)
    }
  }
}

Create routes

Now that we are done with all the required functionality we have to create routes for our users and for that, we have two files inside the routes directory namely

  • auth.js: handles register/login routes
  • user.js: protected routes that handles retriving user’s data

Following is the code for the auth.js route file

const express = require('express')
const router = express.Router()

const { register, login } = require('../controllers/auth')

router.post('/register', register)
router.post('/login', login)

module.exports = router

and below one is our user.js route file.

As you can see our route /info is a protected one and that means it can only be accessed with a valid JWT token.

const express = require('express')
const router = express.Router()

const { getUser } = require('../controllers/user')
const { accessTokenValidator } = require('../middlewares/auth')
// accessToken validator middleware

router.get('/info', accessTokenValidator, getUser)

module.exports = router

Testing our JWT authentication in our nodejs app

Now we are all set with the coding section so, we are left only with the Testing part and for that we will be using VS-code extension “rest-client.”

Now the vs-code extension “rest-client” needs a “rest.http” file inside the root directory of our project containing all our requests.

So, inside the “rest. http” file we have.


POST http://localhost:3000/register
Content-Type: application/json

{
    "email": "[email protected]",
    "password": "Qwerty12!"
}

###login
POST http://localhost:3000/login
Content-Type: application/json 

{
    "email": "[email protected]",
    "password": "Qwerty12!"
}

### get user data(protected route)
GET http://localhost:3000/user/info 
Authorization: Bearer 

Last but not least we start the server using

node app.js

Let’s start by registering a new user by requesting our register route.

If our user was successfully created then our response will look something like this.

Response after account creation
Response after account creation

After we have a user let’s try login. in with the user details we just created.

This will return us a JWT token which we can use later to request a JWT protected route.

Response from the server on login
Response from the server on login

Note: currently we have set an expiry time of 1 minute so our token will only be valid for 1 minute. Please try changing it to something like 1hr in case you see an invalid token or something.

Now to use the token we need to pass our token in the header of our request.

Authorization: Bearer “Token_value”

For example:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImU0NTVjMzg5LWVkMDktNGQxMS1hOWNhLTk0MjFmMzA2MWQ1YyIsInVzZXJuYW1lIjoiRGVtbyB1c2VybmFtZSA5ZjA0NmE5MS1lZmI2LTQxYzQtODhjZi01MTExNGExMmY0MDkiLCJlbWFpbCI6InByYWRlZXBAZ21haWwuY29tIiwiaWF0IjoxNjMwNzMxNTU4LCJleHAiOjE2MzA3MzE2MTh9.hjTzdNU1jdvUJq3cIt7-VDJLgMwP8tKqTV_uRIgCiFs

If our token is valid we must have our user data returned like below

Response from our JWT authenticated route in our nodejs application
User data as response

Conclusion

Walah! we have successfully learned how to add JWT authentication in a nodejs application. I hope, I managed to make you understand the basic idea behind the registration and login using JWT. If you people find it useful please don’t forget to appreciate it in the comment section.

If there is any doubt related to this post, please ask in the comment section. Also, don’t forget to share your feedback.

THANK YOU!

Leave a Reply

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

Back To Top