DEV Community

Cover image for How to use JWT Authentication in NodeJs/NextJs
Husnain Mustafa
Husnain Mustafa

Posted on • Edited on

How to use JWT Authentication in NodeJs/NextJs

What is JWT

JSON web token (JWT), pronounced "jot", is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. (Definition from auth0.com)

In a layman's term, it basically provides a way to convert JSON object into a string using the key we provide to it.

The output of JWT, the obtained string, is used to transfer the information between client and server securely.

We can use for authentication, which is mostly done through middleware

What is Middleware

Sometimes we need to modify the request sent by the client. Let's say client is sending us the the user_id of himself, but we do not know the other the information, like email, related to client.

One of of doing it is to fetch the data within the request handler function (as app.post is request handler function in nodejs/express). This is all fine, but the problem with this type of handling is that this makes code somewhat unclean and effects the readability of code.

Other way of doing it is to make a middleware, which will be a separate function, other than request handler function and preferably in separate file. Middleware accepts 3 arguments, request, response and next.

export const authenticate = (req, res, next)=>{
     ...
}
Enter fullscreen mode Exit fullscreen mode

Request and response are same as request handler functions, i.e, request will contain the data sent by the client, and response will be used to send the response back to client.

Next argument is a function which, when called, move the request to the next handler, i.e, either next middleware function or the final request handler function. It depends upon how you chain the middlewares at request handler function.

For example:

app.get(authenticate, async(req, res)=>{
     ...
}
Enter fullscreen mode Exit fullscreen mode

In the above case, we are calling the authenticate middleware and then our final request handler function.

Above authenticate middleware function looks like this:

export const authenticate = (req, res, next)=>{
if(<authentication successful>){
next()
}
else{
return res.status(401).json({success: false, message: "You are not logged in"})
}
Enter fullscreen mode Exit fullscreen mode

How to use JWT along with middlewares

So first, when user signup or login, we need to generate the JWT with user details, mostly with user id.

This is what my signup looks like:

import Users from "../../backend/models/Users"
import dbConnect from "../../backend/utils/dbConnect"
import { signToken } from "../../backend/utils/jwt"


export default async function handler(req, res) {

  if(req.method != "POST")
    return res.status(404).json({success: false, message: "Method Not Found"})


  if(!req.body.email || !req.body.password || !req.body.name)
    return res.status(400).json({success: false, message: "Please Provide Email, Password and Name"})

  await dbConnect()

  const {email, password, name} = req.body

  try{

  let userFound = await Users.findOne({email})
  if(userFound)
    return res.status(401).json({success: false, message: "Email Already Registered"})

  let user = new Users({email, password, name})
  await user.save()

  let jwtToken = signToken({id: user.id}, process.env.NEXT_PUBLIC_JWT_SECRET)
  return res.status(200).json({success: true, data: {token: jwtToken}})

  }
  catch(err){
    console.log(err)
    return res.status(500).json({succes: false, message: "Some Error Occured In Server, Error: " + err})
  }

}
Enter fullscreen mode Exit fullscreen mode

The line:
let jwtToken = signToken({id: user.id}, process.env.NEXT_PUBLIC_JWT_SECRET)
returns a string (jwt toke) based on the object provided as first argument and the key provided as second argument (process.env.NEXT_PUBLIC_JWT_SECRET is key present in .env file, an environment variable)

We need to send jwt token, (string returned by signToken), to the client and the client will save it as cookie. And with every request, client will send this jwt as authorization in header as 'Bearer jwt_token'.

Our authenticate middleware function looks like this:

import jwt from 'jsonwebtoken'

export const authenticate = (req, res, next)=>{
    if(!req.headers.authorization)
        return res.status(401).json({success: false, message: "User Not Authorized"})

    let token = req.headers.authorization

    // Bearer token
    token = token.split(" ")[1]
    try{
        let user = jwt.verify(token, process.env.NEXT_PUBLIC_JWT_SECRET)
        req.user = user
        next()
    }
    catch(err){
        return res.status(401).json({success: false, message: "User Not Autorized"})
    }
}
Enter fullscreen mode Exit fullscreen mode

You can see that, authentication middleware function is extracting the req.headers.authorization which contains our JSON web token, sent by client in this format: "Bearer jwt_token"
So, we are getting the token by splitting the string with "white space" as delimiter and extracting the string at 2nd index, which is our JWT.

Now we verify it using jwt.verify function, which takes jwt token as first argument and secret key as second argument.

This will return the object that we provided while creating the jwt token.

We will use this object and store in request so that it can be used by next request handler function by simply:
req.user = user and then we will call next()

In case, jwt does not verify the token provided by client, we will simply response back, within the authentication middleware function, as
return res.status(401).json({success: false, message: "User Not Autorized"})

That's it, Hope it helped you out

Edit: Do not name your JWT secret env as NEXT_PUBLIC_JWT_SECRET, because NEXT_PUBLIC prefix tell nextjs to expose the variable on client side. And we should never expose secrets on client side. Rather name your JWT env as JWT_SECRET and access it at server side by process.env.JWT_SECRET

Top comments (3)

Collapse
 
marcel_wolf_e70cec0dc6e50 profile image
Marcel Wolf

I think you NEVER should flag your JWT secret as env with NEXT_PUBLIC_JWT_SECRET.
NEXT_PUBLIC tells next to bundle the env into client code, which is like no auth with jwt.

Collapse
 
husnain profile image
Husnain Mustafa

Agreed

Collapse
 
jagroop2001 profile image
Jagroop Singh

Nice share !!