DEV Community

Prathviraj H
Prathviraj H

Posted on

Secure Authentication in CampusX Using JWT

Introduction

Authentication is a critical part of any web application. In CampusX, we use JWT (JSON Web Tokens) to handle user authentication efficiently and securely. JWT provides a stateless way to verify users while minimizing database queries.

This blog explains:

  • What JWT is and why it's important.
  • How CampusX uses access and refresh tokens.
  • How token refreshing ensures seamless user experience.

What is JWT?

JWT is a compact and self-contained token format used for securely transmitting information between parties as a JSON object. It consists of three parts:

  1. Header – Contains metadata like token type and signing algorithm.
  2. Payload – Stores claims (user information, expiry time, etc.).
  3. Signature – Ensures token integrity using a secret key.

Example of a JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiIxMjM0IiwiaWF0IjoxNjExNjE2MDB9
.4xvNcTkORHj9xE8TftRkEw7sdJjxUX9PYG6xRJOnhZk
Enter fullscreen mode Exit fullscreen mode

JWT is commonly used for authentication because it eliminates the need for session storage and repeated database queries.


Access & Refresh Tokens in CampusX

CampusX uses access tokens for authentication and refresh tokens for renewing access tokens without requiring the user to log in again.

1. Generating Tokens

const generateAccessAndRefreshTokens = async (userId) => {
  try {
    const user = await User.findById(userId);
    const accessToken = user.generateAccessToken();
    const refreshToken = user.generateRefreshToken();

    user.refreshToken = refreshToken;
    await user.save({ validateBeforeSave: false });

    return { accessToken, refreshToken };
  } catch (error) {
    throw new ApiError(
      STATUS_CODES.INTERNAL_ERROR,
      "Error generating access and refresh tokens"
    );
  }
};
Enter fullscreen mode Exit fullscreen mode

Here, the user's access and refresh tokens are generated and stored securely.


JWT Authentication Middleware

The verifyJWT middleware protects routes by verifying JWT tokens before allowing access.

export const verifyJWT = AsyncHandler(async (req, res, next) => {
  try {
    const token = req.cookies?.accessToken || req.header("Authorization")?.replace("Bearer ", "");
    if (!token) {
      throw new ApiError(401, "Unauthorized request");
    }

    const decodedToken = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET);
    const user = await User.findById(decodedToken._id).select("-password -refreshToken");
    if (!user) {
      throw new ApiError(402, "Invalid access token");
    }

    req.user = user;
    next();
  } catch (error) {
    throw new ApiError(401, "Token expired");
  }
});
Enter fullscreen mode Exit fullscreen mode

This ensures only authenticated users can access protected resources.


Refreshing Expired Tokens

If the access token expires, CampusX uses the refresh token to generate a new access token, avoiding user logouts.

const refreshAccessToken = AsyncHandler(async (req, res) => {
  const incomingRefreshToken = req.cookies.refreshToken || req.body.refreshToken;
  if (!incomingRefreshToken) {
    throw new ApiError(STATUS_CODES.UNAUTHORIZED, "Refresh token not found");
  }

  try {
    const decodedToken = jwt.verify(incomingRefreshToken, process.env.REFRESH_TOKEN_SECRET);
    const user = await User.findById(decodedToken._id);

    if (!user || incomingRefreshToken !== user.refreshToken) {
      throw new ApiError(STATUS_CODES.UNAUTHORIZED, "Invalid refresh token");
    }

    const { accessToken, refreshToken } = await generateAccessAndRefreshTokens(user._id);
    res
      .status(STATUS_CODES.OK)
      .cookie("accessToken", accessToken, { httpOnly: true, secure: true })
      .cookie("refreshToken", refreshToken, { httpOnly: true, secure: true })
      .json(new ApiResponse(STATUS_CODES.OK, { accessToken, refreshToken }, "Access token refreshed"));
  } catch (error) {
    throw new ApiError(STATUS_CODES.INTERNAL_ERROR, "Error refreshing access token: " + error.message);
  }
});
Enter fullscreen mode Exit fullscreen mode

Fetching User on Page Refresh

CampusX calls fetchUser on every page load to refresh expired access tokens.

const fetchUser = async () => {
  try {
    const res = await axiosInstance.get("/users/current-user");
    setUser(res.data?.data?.user);
  } catch (err) {
    if (err.response?.status === 401) {
      setUser(null);
    }
    if (err.response?.status === 403) {
      console.log("Access Token Expired, Refreshing...");
      try {
        await axiosInstance.post("/users/refresh-token");
        return fetchUser(); // Retry the original request
      } catch (refreshErr) {
        toast.error("Session expired, Please login again!");
      }
    }
    throw err;
  }
};
Enter fullscreen mode Exit fullscreen mode

How it Works:

  1. Calls /users/current-user to check if the user is logged in.
  2. If the access token is expired (403 error), it tries to refresh it using /users/refresh-token.
  3. If refreshing fails, prompts the user to log in again.

Conclusion

JWT authentication in CampusX ensures:
✅ Secure user authentication.

✅ Efficient token-based session management.

✅ Seamless token refresh without re-login.

By implementing access & refresh tokens, CampusX provides a smooth user experience while maintaining strong security. 🚀

🔹 Try implementing JWT authentication in your projects!

Top comments (0)