DEV Community

Cover image for πŸ‘₯ How I Built a Secure and Clean User Impersonation Feature (ReactJS + NodeJS)
Akash Shukla
Akash Shukla

Posted on • Edited on

πŸ‘₯ How I Built a Secure and Clean User Impersonation Feature (ReactJS + NodeJS)

Hello Everyone, Welcome! Today I'll share something really useful.

Have you ever needed to see exactly what a user is seeing β€” without asking for their password?
Whether you're debugging an issue, providing customer support, or verifying role-based access β€” sometimes, the best way to help is by temporarily stepping into the user's shoes.

That’s where user impersonation comes in β€” and in this article, I’ll show you how to build it securely and cleanly using React, Node.js, and JWT.

What is user impersonation?
It’s a feature that allows admins to temporarily "become" another user, so they can:

  • Debug issues in the app
  • Help users in real-time
  • View permissions and flows as that user

In this article, I’ll walk you through how I built a secure impersonation system using:

  • βœ… JWT (JSON Web Token)
  • βœ… React (Frontend)
  • βœ… Node.js + Express (Backend)
  • βœ… HTTP Only Cookies for session control

🧠 Understanding the Concept First

Let’s take a real-life example:

Imagine you’re a Admin of an App. A customer says:

"I can’t find the report tab on my dashboard."

Now, instead of guessing or asking for screenshots, you can:

  1. Click "Impersonate User"
  2. Instantly log in as that user
  3. See the exact issue
  4. Help them β€” fast!

But impersonation also has risks, so it must be:

  • βœ… Secure: Only admins should do it
  • βœ… Trackable: You should know who did what
  • βœ… Reversible: Admin can exit anytime

πŸ”„ Flow Diagram

🧭 Step-by-Step Impersonation Flow

  1. βœ… Admin clicks "Impersonate" on the user list.
  2. πŸ” Backend generates a special JWT containing both:
    • Target user’s data
    • isImpersonating: true and impersonatedBy (admin info)
    • The backend sets this JWT as an HTTP-only cookie, ensuring it's securely managed by the server and not accessible via client-side JavaScript.
  3. πŸͺ Frontend sets the isImpersonating: true and redirects to the impersonated user’s dashboard.
  4. πŸ‘€ Dashboard detects impersonation flag (from cookies) and shows a banner like: "You're impersonating John Doe".
  5. 🚫 Admin clicks "Stop Impersonation", which:
    • Calls stop API
    • Resets JWT back to the original admin
    • Redirects to admin dashboard

πŸ› οΈ Let's Implement It (Step-by-Step)


πŸ”Ή Step 1: Trigger Impersonation from React

We hit an API to start impersonating a user and save the JWT in cookies.

const startUserImpersonation = (userId: string) => {
  impersonateMutation.mutate(userId, {
    onSuccess: ({ data }) => {
      const { user, isImpersonating } = data;

      saveUserCookies({ roles: user.roles });
      Cookies.set('IMPERSONATION_ACTIVE', String(isImpersonating));

      toast.success("Impersonation started.");
      router.replace(resolveUserHome(user.roles)); 
    },
    onError: () => toast.error("Failed to impersonate user."),
  });
};
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή Step 2: Backend API to Start Impersonation

async function startImpersonation(req, res) {
  const user = await UserModel.findById(req.params.id);
  if (!user || user.roles.includes("admin")) {
    return res.status(400).json({ message: "Cannot impersonate this user." });
  }

  const tokenPayload = {
    _id: user._id,
    email: user.email,
    roles: user.roles,
    impersonationActive: true,
    impersonatedBy: {
      _id: req.user._id,
      email: req.user.email,
      roles: req.user.roles,
    },
  };

  const token = createJWT(tokenPayload, 1); // 1 hour expiry time
  res.cookie("auth_token", token, jwtCookieOptions);

  res.status(200).json({ message: "Impersonation started", data: { user, isImpersonating: true } });
}
Enter fullscreen mode Exit fullscreen mode

Note: Make sure this API is protected by appropriate middleware, so that only authorized users (e.g., admins) have access to perform impersonation.


πŸ”Ή Step 3: Show "Stop Impersonation" Banner in React

{isImpersonating && (
  <div className="fixed top-2 left-1/2 -translate-x-1/2 bg-gray-900 text-white px-4 py-2 rounded">
    You're impersonating {userName}
    <button onClick={stopImpersonation} className="ml-2 underline">Stop</button>
  </div>
)}
Enter fullscreen mode Exit fullscreen mode

And here’s the handler:

const stopImpersonation = () => {
  stopImpersonationMutation.mutate(undefined, {
    onSuccess: ({ data }) => {
      saveUserCookies({ roles: data.user.roles });
      Cookies.remove('IMPERSONATION_ACTIVE');
      toast.success("Impersonation stopped.");
      router.replace(resolveUserHome(data.user.roles));
    },
  });
};
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή Step 4: Middleware to Decode JWT

Here we check if the token has impersonation info.

async function decodeAuthToken(req, res, next) {
  const token = req.cookies.auth_token;
  if (!token) return next();

  const decoded = await verifyJWT(token);

  if (decoded.impersonationActive) {
    req.user = decoded.impersonatedBy;
    req.isImpersonating = true;
    return next();
  }

  // Normal user session
  req.user = await UserModel.findById(decoded._id);
  next();
}
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή Step 5: Stop Impersonation on the Backend

async function stopImpersonation(req, res) {
  if (!req.isImpersonating) {
    return res.status(400).json({ message: "Not currently impersonating." });
  }

  const adminUser = await UserModel.findById(req.user._id);
  const token = createJWT(adminUser);
  res.cookie("auth_token", token, jwtCookieOptions);

  res.status(200).json({ message: "Impersonation ended", data: { user: adminUser } });
}
Enter fullscreen mode Exit fullscreen mode

βœ… Bonus Tips to Improve It

To take this feature from β€œcool” to β€œproduction-ready,” here are some pro tips:


πŸ“Œ Add Logs

Log who impersonated whom:

logger.info(`${req.user.email} impersonated ${user.email} at ${new Date()}`);
Enter fullscreen mode Exit fullscreen mode

🚫 Block Dangerous Actions

For example, disallow deleting users while impersonating:

if (req.isImpersonating) {
  return res.status(403).json({ message: "This action is not allowed during impersonation." });
}
Enter fullscreen mode Exit fullscreen mode

🧾 Save Audit Trails

(Optional) Store impersonation events in DB:

  • Who started
  • When
  • Actions taken

🧩 Final Thoughts

This impersonation system helps:

  • Debug user issues faster
  • Support customers better
  • View the app like your users do

It’s simple, powerful β€” and when done securely β€” a huge win for your team and product.

Top comments (1)

Collapse
 
svijaykoushik profile image
Vijay Koushik, S. πŸ‘¨πŸ½β€πŸ’»

This is a fantastic introduction to user impersonation, especially for developers looking to bootstrap the feature. The step by step breakdown and code snippets make it easy to grasp the core concept.

It would have been even more valuable to include links to resources, or for further reading on secure implementation, best practices and potential pitfalls.

This could help readers to move towards "production ready"

Looking forward to more insights from you