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)