Getting Started with JWT Tokens: Basics You Need to Know

Getting Started with JWT Tokens: Basics You Need to Know

This blog is a beginner-friendly introduction to JWT (JSON Web Tokens). It explains the basics of what JWTs are, how they work, and why they are used for secure authentication in modern applications. You’ll learn about the structure of a JWT , library for JWT and common use cases such as user login and API authentication.

What is a JWT?

A JSON Web Token (JWT) is a compact, URL-safe token used to securely transmit information between parties. It’s commonly used for authentication and information exchange in web applications.


Structure

A JSON Web Token is nothing but a long encoded text string which is made up of three parts separated by a period. These parts are:

  1. Header

  2. Payload

  3. Signature

A typical JWT looks like the following:

header.payload.signature

A JWT is composed of three parts:

  1. Header: Contains metadata about the token, such as the algorithm used for signing.

  2. Payload: Contains claims — pieces of information (like user ID or roles).

  3. Signature: Verifies the token’s integrity and authenticity.

Example of a JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEyMywicm9sZSI6ImFkbWluIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c


Why Use JWT?

Here are some reasons developers love JWT:

  • Stateless Authentication:This means that the server does not need to store any session data, which makes the system more scalable.

  • Compact: Being lightweight makes JWTs easy to pass in URLs, headers, or cookies.

  • Secure: Signed tokens ensure data integrity and, optionally, confidentiality.

  • Cross-domain authentication: JWT is great for applications where the client and server are in different domains.


How Does JWT Work?

  1. User Login:

    • A user logs in with credentials (e.g., username and password).
  2. Token Generation:

    • The server validates the credentials and generates a JWT containing user-specific data (e.g., user ID).
  3. Token Storage:

    • The client stores the JWT (commonly in localStorage or cookies).

    • cookies are more secure because they can be HttpOnly, but localStorage is easier to access from JavaScript.

  4. Token Usage:

    • For subsequent requests, the client includes the JWT in the Authorization header:

        Authorization: Bearer <your-token>
      
  5. Token Verification:

    • The server verifies the token’s signature to ensure it’s valid and unaltered.

Installation

To start working with JSON Web Tokens (JWT) in your Node.js application, you need to install the jsonwebtoken library. This library provides the necessary functions to generate, verify, and decode JWTs.


npm install jsonwebtoken // for npm
yarn add jsonwebtoken // for yarn

After installing jsonwebtoken import the jwt from ‘jsonwebtoken’ in your file.

import jwt from 'jsonwebtoken';

Additional Setup:

You also need to make sure you have a secret key (JWT_SECRET) to sign and verify the JWTs. This key should be kept secure and not exposed in your codebase. You can store it in an environment variable (process.env.JWT_SECRET), for example:

  1. Create a .env file in the root of your project.

  2. Add your secret key to the .env file:

     JWT_SECRET=your-secret-key
    
  3. Use a package like dotenv to load the .env file into your Node.js application:

     npm install dotenv // for npm
     yarn add dotenv // for yarn
    
  4. Import dotenv and load the environment variables at the start of your application:

     import dotenv from 'dotenv';
     dotenv.config(); // Loads the .env file into process.env
    

Now you're ready to start using JWT in your project. You can generate tokens with jwt.sign(), verify them with jwt.verify(), and decode them with jwt.decode().

There are three main funtion used from jwt.

  1. jwt.sign ( Payload, JWT_SECRET, Expire Time ): It is used to generate the JWT Token, where

    • Payload: The data you want to include in the JWT. It can be any valid JSON data you want to encode in the token.

    • JWT_SECRET: A secret key used to sign the JWT and verify its authenticity. It should be kept secure and only known to the parties involved in the token exchange.

    • Expire Time: The duration after which the JWT will expire and no longer be valid.

example :

   const JWT_SECRET = process.env.JWT_SECRET;

   const accessToken = jwt.sign({ email: UserExist.email }, JWT_SECRET, {
      expiresIn: '5m',
    });

We create the JWT with the user’s email as the payload, using the secret key to sign it, and set it to expire in 5 minutes.

  1. jwt.verify ( Token, JWT_SECRET, (err, decoded) => { } ): It is used to verify and decode the JWT Token, where

    • Token: The JWT token you want to verify. It is a string containing the payload and signature of the JWT.

    • JWT_SECRET: A secret key used to sign the JWT and verify its authenticity. It should be kept secure and only known to the parties involved in the token exchange.

    • (err, decoded) => { }: A callback function called when the verification process is complete. It takes two arguments: err and decoded.

example:

  jwt.verify(refreshToken, JWT_SECRET, (err, decoded) => {
    if (err) {
      return res
        .status(StatusCodes.FORBIDDEN)
        .json({ message: 'Invalid refresh token' });
    }
  1. jwt.decode(Token): It is used to decode a JWT Token, where

    • Token: The JWT token you want to decode. It is a string containing the payload and signature of the JWT. Unlike jwt.verify(), jwt.decode() does not verify the signature but simply extracts the payload from the token.
const decoded = jwt.decode(token);

Error Handling in JWT Verification

When verifying a JWT, it's important to handle errors gracefully. Common errors include invalid tokens, expired tokens, or malformed tokens. You can handle these errors using try/catch blocks or by handling specific error types in the callback function passed to jwt.verify.

Example using try/catch with jwt.verify:

import jwt from 'jsonwebtoken';

const JWT_SECRET = process.env.JWT_SECRET;

const verifyToken = (req, res, next) => {
  // Get token from Authorization header
  const token = req.headers['authorization']?.split(' ')[1];

  if (!token) {
    return res.status(403).json({ message: 'No token provided' });
  }

  try {
    // Verify the token
    const decoded = jwt.verify(token, JWT_SECRET);
    req.user = decoded; // Attach decoded user info to the request object
    next(); // Move to the next middleware or route handler
  } catch (err) {
    if (err.name === 'JsonWebTokenError') {
      return res.status(400).json({ message: 'Invalid token' });
    }
    if (err.name === 'TokenExpiredError') {
      return res.status(401).json({ message: 'Token has expired' });
    }
    return res.status(500).json({ message: 'Internal server error' });
  }
};

Explanation:

  • Error Handling: In this example, we first check if the Authorization header exists and extract the token. If no token is provided, we send a 403 response indicating that the request is forbidden due to the lack of a token.

  • try/catch Block: The try/catch block is used to handle potential errors during JWT verification.

    • If the token is invalid, jwt.verify will throw a JsonWebTokenError, and we respond with a 400 status code.

    • If the token has expired, a TokenExpiredError will be thrown, and we respond with a 401 status code, indicating that the token is no longer valid.

    • If an unexpected error occurs, we send a generic 500 server error message.

Set Token in Cookies

  const accessToken = jwt.sign({ email: UserExist.email }, JWT_SECRET, {
      expiresIn: '5m',
    });

  res.cookie('saroj-x-access-token', accessToken, {
      maxAge: 1000 * 60 * 5, // expire after 15 minutes
      httpOnly: true, // Cookie will not be exposed to client side code
      secure: true, // use with HTTPS only
      sameSite: 'none', //
    });

Tips for Secure JWT Implementation

  1. Use HTTPS: Always transmit JWTs over secure connections.

  2. Set Expiry Times: Define short expiration times to limit token misuse.

  3. Validate Signatures: Ensure tokens are signed with a strong, secret key.

  4. Avoid Sensitive Data: Never store sensitive information (like passwords) in the payload.

  5. Implement Token Blacklisting: Use blacklists for compromised tokens.

Conclusion

JWTs are a powerful tool for modern authentication systems, offering a stateless and secure way to manage user sessions and API access. By understanding the basics and following best practices, you can confidently implement JWTs in your projects.

Ready to try it out? Start with your favorite framework or library and integrate JWT into your authentication workflow!