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: trueandimpersonatedBy(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: trueand 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 (1)
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