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:
- Click "Impersonate User"
- Instantly log in as that user
- See the exact issue
- 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
- ✅ Admin clicks "Impersonate" on the user list.
- 🔐 Backend generates a special JWT containing both:
- Target user’s data
-
isImpersonating: true
andimpersonatedBy
(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.
- 🍪 Frontend sets the
isImpersonating: true
and redirects to the impersonated user’s dashboard. - 👀 Dashboard detects impersonation flag (from cookies) and shows a banner like:
"You're impersonating
John Doe
". - 🚫 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."),
});
};
🔹 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 } });
}
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>
)}
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));
},
});
};
🔹 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();
}
🔹 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 } });
}
✅ 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()}`);
🚫 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." });
}
🧾 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 (0)