Add Support Impersonation to an AI-Built App Safely
An AI builder will hand you a view-as-user feature that is really a login-as-anyone endpoint. The eight constraints that make it safe, plus six tests.
Add Support Impersonation to an AI-Built App Safely
Support impersonation, the view as user button that lets your support team see exactly what a customer sees, is the single most useful debugging feature a small SaaS can have and the one most likely to become a quiet backdoor. Ask an AI app builder for it and you will get a working version in about ninety seconds. That version will almost certainly log the support agent in as the customer with no separate session type, no banner, no audit trail and no expiry, which is a login-as-anyone endpoint sitting in your codebase.
Here is the spec that makes it safe, the prompt that gets you there, and the six tests that prove it.
Why the default implementation is dangerous
The naive version does one thing: it sets the session user ID to the target user. Four problems follow immediately.
It is indistinguishable from a real login. Your app cannot tell the difference, so neither can your logs, your analytics, your billing events or your fraud checks. Something happened in the customer's account and nobody can say who did it.
There is no boundary on what the agent can do. Impersonation inherits the customer's full permissions, which means deleting their data, changing their email, cancelling their subscription and reading their private messages are all one click away and indistinguishable from the customer doing it themselves.
It never ends. The session persists like any other. An agent who impersonated a customer on Monday may still be inside that account on Thursday, having forgotten.
Nobody can see it. Not the customer, and often not the agent either, who forgets which account they are in and replies to a support ticket from inside someone else's session.
None of this is the builder being careless. It gave you what you asked for. The safety lives entirely in the requirements you did not state.
The spec to hand your builder
Eight requirements. Give them as constraints, not suggestions, because a model will drop anything phrased as a preference.
A distinct session type. The session carries both the real actor and the impersonated user, for example acting_user_id alongside user_id. Every part of the app can tell the difference and so can every log line.
Read-only by default. Impersonated sessions cannot write. Any mutation is refused at the server, not hidden in the interface. Roughly nine support investigations in ten need to see what the customer sees, not to act as them.
An explicit, narrow write mode. When writing is genuinely required, it is a separate elevation with its own reason field, its own approval and its own log entry. Enumerate what it may touch.
Hard-blocked actions, always. Changing email or password, deleting the account, exporting all data, changing billing details, viewing raw payment credentials. These stay blocked even in write mode. This is what stops impersonation being an account-takeover primitive.
A persistent visual banner. Every page, impossible to dismiss, naming the impersonated account and offering a one-click exit. This prevents the most common real-world incident, which is not malice but an agent forgetting where they are.
A time limit. Thirty minutes, expiring automatically, no silent renewal.
An immutable audit record. Who impersonated whom, when, why, for how long, and every action taken. Append-only, and not editable from inside the application. Our guide to adding an audit log to an AI-built app covers the storage shape.
A permission of its own. Impersonation is a distinct permission granted to named staff roles, not something every admin inherits by default. If you have not yet set up role-based permissions, do that first, because this feature is not safe without them.
The data model
Two pieces. An impersonation_sessions record holding actor user ID, target user ID, reason text, started timestamp, expiry timestamp, ended timestamp, write mode flag, and the linked support ticket if there is one. And a field on every audit row recording the acting user, populated from the session rather than from the request.
That last detail is the one that usually gets missed. If your audit log reads the user from the current session without asking who is really behind it, the entire record says the customer did it. Write it as: the audit trail records the human, the application records the account.
The prompt
Add support impersonation with these constraints, and do not simplify any of them:
Impersonation creates a session carrying both acting_user_id and user_id. Never overwrite the real user identity.
Impersonated sessions are read-only. Reject all write requests server-side with a clear error. Do not rely on hiding buttons.
Block these actions in every mode: change email, change password, delete account, change billing details, export all data, view payment credentials.
Render a fixed banner on every page showing the impersonated account and an exit control.
Sessions expire after 30 minutes with no renewal.
Every impersonation start, end and action writes an append-only audit row recording acting_user_id and reason.
Gate the feature behind a dedicated permission, not the general admin role.
A reason string is required to start a session. Reject empty or whitespace-only reasons.
Then ask one follow-up question, which is where most of the value comes from: list every route in this codebase that writes data and show me how each one is prevented from executing under an impersonated session. A builder that answers with three of your eleven write endpoints has just told you the enforcement is in the interface rather than the server.
Six tests before this goes near a customer
Start an impersonation session, then call a write endpoint directly, bypassing the interface entirely. It must fail at the server.
Check your audit rows. Actions taken during impersonation must name the support agent, not the customer.
Wait out the expiry, then make a request with the same session. It must be rejected rather than renewed.
Attempt each blocked action while in write mode. All must fail.
Confirm the banner survives a full page reload and a navigation to a deep route.
Have someone without the impersonation permission try to reach the endpoint by URL. Not a hidden button, an actual permission check.
Test one is the important one, and the one people skip. The failure mode is always the same: the builder hides the save button and calls it read-only, while the API endpoint behind it happily accepts a request from any authenticated session.
The part that is not code
Decide, in writing, when impersonation is allowed at all. A reasonable default for a small team: only with an open support ticket from that customer, or with their explicit consent in writing. Say so in your privacy policy, because in most jurisdictions staff access to a customer account is exactly the sort of processing a customer expects to be told about.
Then read your own audit log once a month. Not to catch anyone, but because an unread log is a compliance artefact rather than a control, and the usual finding is boring and useful: one agent using impersonation twenty times more than everyone else because they have not learned the actual debugging tools. Customers who ask what your staff can see deserve a straight answer, and the same reasoning applies to what you let third-party tools reach.
Frequently asked questions
Should the customer be notified when someone impersonates their account?
For consumer products with sensitive data, yes, and it builds more trust than it costs. For business tools where an admin raised the ticket, a visible entry in an account activity log is usually enough. What you should not do is make it invisible by design.
Can I reuse my normal login flow for this?
No, and this is the most common shortcut. Reusing the login path means any weakness in impersonation becomes a weakness in authentication. Keep it a separate code path with its own session type, and keep two-factor authentication on the staff account that starts it.
What about read-only access to a customer's data without a full session?
Often the better answer. A support view that reads the customer's records into your own admin interface avoids the impersonation risk entirely and covers most of the need. Build this first and add impersonation only for the cases it cannot handle, usually interface-specific bugs you cannot reproduce.
How does this connect to support tickets?
Tie the reason field to a ticket ID and the audit trail becomes self-documenting: every session has a customer-visible justification. If you have a support ticket system in the app, link the two records at creation time rather than retrofitting it later.
For the wider set of practices on shipping features like this, see our guide to building an app with AI.
How did this land?
About the author

Developer Advocate
Steve builds something with Swarmz every week and writes up what worked, what broke, and what he'd do differently. Tutorials and hands-on guides are his lane.


