JSON web token (JWT) authentication in NodeJS applications (2024)

A JSON Web Token, popularly known as JWT, is an open standard that defines a compact way for securely sharing information between two parties: a client and a server.

Unlike the traditional server-side sessions — which save a session id in memory and return it to the client — this standard creates a self-contained and digitally signed token that is verified each time a request is made.

In this article, you will learn how to add a layer of security to your application by using JWTs to authenticate requests made to your application.

Structure of a JWT

A JWT consists of a header, a payload, and a signature.

The header contains metadata about the token type and the type of algorithm it is secured with. The type of token, which is a JWT, and the signing algorithm being used, which can either be a hash-based message authentication code (HMAC), a secure hash algorithm (SHA256), or a Rivest-Shamir-Adleman (RSA) algorithm.

For example:

{ "alg": "HS256", "typ": "JWT"}

The payload contains verifiable statements about users, such as their identity and access permissions, known as claims:

{ "email": "[emailprotected]", "iat": 1667134212, "exp": 1667136012}

The signature is a string generated from the signing algorithm used to verify that the received JWT has not been tampered with. When working with JWTs, you must check its signature before storing and using them:

HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

The three parts of the JWT are concatenated, separated by a dot (.) and encoded in Base64Url to form the JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImZpcnN0QGdtYWlsLmNvbSIsImlhdCI6MTY2NzEzNDIxMiwiZXhwIjoxNjY3MTM2MDEyfQ.55_3rFnl2KMQ0tRLusWY6EExHTGjVSbNPX8VFMSQTlQ

Setting up your development environment

To follow this tutorial, you will need to clone this GitHub repository, which contains boilerplate code for a simple user-authentication model.

After cloning the repository, run the following command to install all the dependencies:

npm install

The dependencies installed include:

Note: To follow along with this tutorial, you’ll need to connect your application to a MongoDB database. If you have Docker installed, you can use the docker-compose.yml included with docker compose up. Just be sure to change the left side of the volume path (i.e., /Users/andrew/techne/node-jwts/mongo) to be something on your local drive. It will load a local database for you in Docker corresponding to MONGODB_URI=mongodb://mongo:mongo@localhost:27017 in your .env file.

Creating a JSON web token

First, open your userRoute.js file and import jsonwebtoken into it like so:

const jwt = require("jsonwebtoken");

Next, you need to create a JWT and assign it to a user when they successfully login into your application.

You can create and sign a JWT using the jsonwebtoken.sign() method. This method takes three arguments, a payload, a token secret, and a configuration object.

The payload can be user data, such as the username or email.

The token secret is a random string used to encrypt and decrypt data. This string should be as long and as random as possible to make the authorization process harder for malicious users.

To ensure the string is random and unique, use the inbuilt Node.js crypto module to generate it. To achieve this, open a new terminal and run the command below:

node

The command in the code block above allows you to write Node.js in your terminal.

Next, run the following command in your node terminal:

>require("crypto").randomBytes(32).toString("hex") //'4c0d608098b78d61cf5654965dab8b53632bf831dc6b43f29289411376ac107b'

The command above generates a 32-byte string.

After that, copy the generated string and add it to a .env file like this:

//.envJWT_SECRET=4c0d608098b78d61cf5654965dab8b53632bf831dc6b43f29289411376ac107b

Next, in your sign-in route handler, call the jwt‘s sign method and pass in the payload (email), the secret, and the expiry. Store the result in a variable like so:

const token = jwt.sign({email}, process.env.JWT_SECRET, { expiresIn: "1800s" }) return res .status(200) .json({ message: "User Logged in Successfully", token });

Note: You can only set an expiry if the payload is an object literal.

Authenticating a JSON web token

Using Express’s middleware functionality, you can create a middleware that protects routes from unauthorized access by checking and authenticating a JWT.

Add the code block below to your userRoute.js file to create the authentication middleware:

const authenticateToken = (req, res, next) => { const authHeader = req.headers["authorization"]; //Extracting token from authorization header const token = authHeader && authHeader.split(" ")[1]; //Checking if the token is null if (!token) { return res.status(401).send("Authorization failed. No access token."); } //Verifying if the token is valid. jwt.verify(token, process.env.JWT_SECRET, (err, user) => { if (err) { console.log(err); return res.status(403).send("Could not verify token"); } req.user = user; }); next();};

In the authenticateToken middleware above, you first check for an authorization header. Then, you extract the token from the authorization headers if it exists by turning the authorization header to an array and taking the element at index 1. The authorization headers come in a “Bearer token“ format, i.e., the authorization header would be in this format: “Bearer eyxxx.tyxxxx.zyxxx”, which is why splitting it into an array and taking the element at index 1 gives you the token. Then, you check if the token exists and throw an error if it doesn’t.

Finally, you verify the token’s authenticity using the jwt.verify() method. The verify method takes the extracted token, your JWT secret, and a callback as arguments. Once the token is verified, save the user’s details to the request object to keep track of the user’s activities.

You can apply this middleware to all routes you want to protect from unauthorized access:

router.get("/test", authenticateToken, (req, res) => { res.send("User authorized");});

Testing protected routes

To test if your protected routes are actually only accessible to authorized users, you can use Postman or any other API client to make requests to the routes. This tutorial will feature the use of Postman. First, add the code block below to your userRoutes.js file:

router.get("/test", authenticateToken, (req, res) => { res.send("Token Verified, Authorizing User...");});

The route above is protected by the authenticateToken middleware you created earlier, which implies that the route cannot be accessed without an access token. Then bring your application online with the node app.js command.

Next, on Postman, try to access the protected route (http://localhost:3000/test) without logging into your application to retrieve the token. You should get a 401 Unauthorized response, as shown in the image below:

JSON web token (JWT) authentication in NodeJS applications (1)

Next, create a new user by making a POST request to http://localhost:3000/sign-up with the fake data below or any data of your choice:

{ "email": "[emailprotected]", "password": "password"}

Now, log into your application using the details you created the user with. After you log in successfully, you will receive the access token as part of the response, as shown in the image below:

JSON web token (JWT) authentication in NodeJS applications (2)

Copy the access token, add it to the authorization header under Bearer Token, and make another request to your protected route (http://localhost/3000/test). If the token is verified, you’ll get a 200 OK response, as shown in the image below:

JSON web token (JWT) authentication in NodeJS applications (3)

Implementing refresh tokens

When a user’s access token expires, the user has to be re-authenticated by the application to obtain a new access token. The re-authentication can be skipped using refresh tokens, which are tokens used to generate a new access token.

To generate a refresh token, you first have to generate a refresh token secret. You can follow the same steps used to generate a JWT secret to generate a refresh token secret:

>require("crypto").randomBytes(32).toString("hex") //'c4a71ea639f6de11f146f21193d91c4518a3c285f1853d44778910d6130a4c03'

Next, copy the generated string and add it to your .env file:

//.envREFRESH_TOKEN_SECRET=c4a71ea639f6de11f146f21193d91c4518a3c285f1853d44778910d6130a4c03

Then, in userRoutes.js, create an in-memory storage like an array for the refresh tokens:

const refreshTokens = []

Next, update your /sign-in route to generate a refresh token along with the JWT access token and save the generated refresh token to the array you created earlier:

const token = jwt.sign({email}, process.env.JWT_SECRET, { expiresIn: "1800s" }) const refreshToken = jwt.sign(email, process.env.REFRESH_TOKEN_SECRET); refreshTokens.push(refreshToken); return res.status(200).json({ message: "User Logged in Successfully", token, refreshToken, });

Your finished /sign-in route handler should look like this:

router.post("/sign-in", async (req, res) => { try { //Extracting email and password from the req.body object const { email, password } = req.body; //Checking if user exists in database let user = await User.findOne({ email }); if (!user) { return res.status(401).json({ message: "Invalid Credentials" }); } //Comparing provided password with password retrieved from database bcrypt.compare(password, user.password, (err, result) => { if (result) { const token = jwt.sign({email}, process.env.JWT_SECRET, { expiresIn: "1800s" }) const refreshToken = jwt.sign(email, process.env.REFRESH_TOKEN_SECRET); refreshTokens.push(refreshToken); return res.status(200).json({ message: "User Logged in Successfully", token, refreshToken, }); } console.log(err); return res.status(401).json({ message: "Invalid Credentials" }); }); } catch (error) { res.status(401).send(err.message); }});

Next, create a new route handler for /token that handles the new access token creation:

router.post("/token", (req, res) => { const refreshToken = req.body.token; if (refreshToken == null) { return res.status(401).send("No refresh token provided!"); } if (!refreshTokens.includes(refreshToken)) { return res.status(403).send("Invalid Refresh Token"); } jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => { if (err) return res.status(403).send("Could not Verify Refresh Token"); const token = jwt.sign({email: req.body.email}, process.env.JWT_SECRET, { expiresIn: "1800s" }) res.json({ accessToken: token }); });});

In the code block above, you first extract the refresh token from the body object. Then, check if the refresh token is null and send a 401 status code if it is. After that, check if the refresh token is from your application by seeing if it’s stored in your array.

Finally, check if the refresh token is valid using the verify method. If the token is valid, create a new access token and return it to the user, else you send a 403 status code.

Conclusion

This tutorial covered how you can add an extra layer of security to your application using JWTs.

Although JWTs are not easily exploitable, they can be exploited if the tokens are stored carelessly in local storage, which can be very hard to revoke. Be sure to be careful when working with these tokens.

Happy coding! If you like content like this, browse the Mattermost Library for more tips and tricks on developing software using open source technologies.

This blog post was created as part of the MattermostCommunity Writing Programand is published under theCC BY-NC-SA 4.0 license. To learn more about the Mattermost Community Writing Program,check this out.

JSON web token (JWT) authentication in NodeJS applications (2024)
Top Articles
Lexus Financial Services Auto Loan Reviews 2024 | Intuit Credit Karma
React useLayoutEffect vs. useEffect Hooks with examples - LogRocket Blog
Evil Dead Movies In Order & Timeline
Knoxville Tennessee White Pages
Nwi Police Blotter
Builders Best Do It Center
Wisconsin Women's Volleyball Team Leaked Pictures
Craigslist Motorcycles Orange County Ca
Erskine Plus Portal
سریال رویای شیرین جوانی قسمت 338
Fear And Hunger 2 Irrational Obelisk
Craiglist Tulsa Ok
Craighead County Sheriff's Department
2020 Military Pay Charts – Officer & Enlisted Pay Scales (3.1% Raise)
China’s UberEats - Meituan Dianping, Abandons Bike Sharing And Ride Hailing - Digital Crew
Moving Sales Craigslist
Exterior insulation details for a laminated timber gothic arch cabin - GreenBuildingAdvisor
Forest Biome
Wsop Hunters Club
Transactions (zipForm Edition) | Lone Wolf | Real Estate Forms Software
Orange Pill 44 291
Sodium azide 1% in aqueous solution
Jordan Poyer Wiki
Sound Of Freedom Showtimes Near Movie Tavern Brookfield Square
Workshops - Canadian Dam Association (CDA-ACB)
Tamil Movies - Ogomovies
Striffler-Hamby Mortuary - Phenix City Obituaries
Craigslist Scottsdale Arizona Cars
APUSH Unit 6 Practice DBQ Prompt Answers & Feedback | AP US History Class Notes | Fiveable
Halsted Bus Tracker
The Venus Flytrap: A Complete Care Guide
Teenbeautyfitness
Palmadise Rv Lot
How does paysafecard work? The only guide you need
Exploring TrippleThePotatoes: A Popular Game - Unblocked Hub
Craigslist In Myrtle Beach
John F Slater Funeral Home Brentwood
Cvb Location Code Lookup
Admissions - New York Conservatory for Dramatic Arts
Autum Catholic Store
Mathews Vertix Mod Chart
Yale College Confidential 2027
American Bully Puppies for Sale | Lancaster Puppies
25 Hotels TRULY CLOSEST to Woollett Aquatics Center, Irvine, CA
Rite Aid | Employee Benefits | Login / Register | Benefits Account Manager
Www Pig11 Net
Goosetown Communications Guilford Ct
Superecchll
91 East Freeway Accident Today 2022
Craigslist.raleigh
Att Corporate Store Location
Latest Posts
Article information

Author: Aracelis Kilback

Last Updated:

Views: 5451

Rating: 4.3 / 5 (64 voted)

Reviews: 87% of readers found this page helpful

Author information

Name: Aracelis Kilback

Birthday: 1994-11-22

Address: Apt. 895 30151 Green Plain, Lake Mariela, RI 98141

Phone: +5992291857476

Job: Legal Officer

Hobby: LARPing, role-playing games, Slacklining, Reading, Inline skating, Brazilian jiu-jitsu, Dance

Introduction: My name is Aracelis Kilback, I am a nice, gentle, agreeable, joyous, attractive, combative, gifted person who loves writing and wants to share my knowledge and understanding with you.