A feature that is often requested is the ability for a privileged user to impersonate another user’s account. If you aren’t familiar with that, it just means that the privileged account wants to be able to see exactly what that other user sees on the same screens when they logon. It is primarily used for support purposes to assist the impersonated account. We’ve designed this feature in different iterations but it comes with a lot of education and security risks that must be addressed.
If you want to skim this article, PLEASE DON’T SKIM THIS! Think about what is being requested. A privileged user wants the ability to see exactly what the impersonated account sees, meaning the privileged user must have the access in the source data for that to happen. Additionally, any change created in the data must be explicitly captured under the privileged user id and not the impersonated account to ensure the integrity of the transaction audits..
Step 1: Privileged user authorizations
This sounds simple since they are “privileged” users, but there are a few things to consider.
Do they have authorization to the data at the database layer (read rights for sure, write possibly)?
Do the users know their accounts can be impersonated? This isn’t as critical for enterprise apps as communication of expectations can be managed easily and there is a basic inherit trust, but when dealing with commercial customers, it is a good idea to disclose that you have that permission. Customers don’t typically like having other people view their data (and could be against the law in some cases!).
A simple check should be made that the privileged users have the needed data authorizations, or this won’t work at all. Also, limit the feature to the roles where it makes sense. For example, you shouldn’t allows someone the ability to impersonate an admin. Careful thought needs to happen when determining how to scope the data and features to be impersonated. I read through Oracle and Microsoft’s documentation about impersonation and an idea that I credit them having is to use a form of two-factor authentication or challenge token in order to activate the feature. You want this feature to be extremely deliberate, so setting up a method where the user must authenticate differently (Google Authenticator widget for example) or send a notice to a second admin that must approve (the two-key holders method) is a great way to lock this feature up.
Step 2: Consider your audit points
If you are building an impersonate feature, the primary concern is to prevent one user from changing data (or viewing data) under the guise of another account. You can imagine the worst-case scenarios of this on your own, but even the most basic violation in traceability is a huge problem. Therefore, you need to create a lot of auditing in this workflow as follows (at a minimum):
When a user activates the impersonation - (Capture which privileged account, which impersonated account, timestamp, and optionally a description that the privileged account types in as justification if this an infrequently used feature)
Each page that they look at - (Page name, date time, privileged account, impersonated account)
Data Edits - (if you are building the feature to also include the ability to make data changes, you need to capture a full regression audit as to what the data was before and after the change, and you could write your own or leverage the “Audit Module” in Mendix. We prefer to write our own to control the performance impact of audits and which fields are needed)
These are the basic audit points, but for your use case you should capture a complete audit trail of every button click, data screen, data export, etc that a user is doing while impersonating another. While you could write this all to system logs and monitor with Splunk or other means, I recommend instead that you build an ImpersonateAudit table to capture the events so you can create in-app reports which are much easier to analyze vs log files.
Step 3: Determine how long the impersonate feature lasts
A privileged user should have to first “activate” the ability to go into impersonate mode, seconded by choosing the user they want to impersonate. By forcing them to opt into the feature, not only are you creating an audit point, but you also set a timer. Usually people impersonating don’t need more than 10 minutes because they are checking on something for their users, but this feature can be useful for testers as well which need more time. You should build a data configuration that can set that expiration as a run-time event.
When the expiration time is reached, the user should be notified and asked if they want to extend another session or not. You can actively poll or show a timer, or you can simply wait until they initiate a button press in the impersonate mode to check if they have time and handle it then. UX designers like to give the user a small heads up such as 1 minute before timeout but how you construct that piece is up to you. The point is that impersonation should expire so a user doesn’t start, leave their workspace and come back, only to forget and continue working in that mode. Which sets up Step 4.
Step 4: UI changes
When a user is impersonating, it is important that the privileged user has some sort of indication that they are in that mode. It could be a simple banner, or the header changes color to Red, or any other number of ways to indicate this mode is active. That helps to avoid any confusion on the privileged user account to know exactly what mode they are in. Using the CSS Selector Helper widget as a way to implement those visual cues.
Step 5: Notifications
If you have workflows that kick off emails/alerts/integrations, make sure you consider those in your feature design. You don’t necessarily have to turn them off, but make sure that you are passing the privileged user account as the initiator of the notification and not the impersonated account where it makes sense. It’s easy to miss this and it can result in some very awkward conversations if you don’t manage this properly.
Step 6: Mendix design of the feature
If you want to build something like this, you could wrap all of your data pages in an “Account, by parameter” data view to pass the impersonated account instead of the logged in privileged user account when the feature is active. All of the widgets and data shown on the pages should refer to the Account in the data retrieves that are used for the page.
In the Admin pages, you should build the feature to set the impersonate timer and pull audit reports.
If you allow your privileged user to write data in addition to the basic read, make sure the committed changes also refer to the privileged user account either through the described audits or by committing as the Privileged user. Creating that latter is a much, much more complicated situation so it is indeed easier to commit “as” the impersonated account but then has the inherit risk (and potentially unacceptable situation) that the data shows one account made changes when it didn’t. What I recommend to customers is to keep the impersonate as a read-only feature so that they can coach the impersonated account how to properly address it. Sometimes that isn’t enough, so we do everything we can to make the commits in the data using the Privileged account instead of the Impersonated account. That is 100% the case for integrations that stem from a transaction. You should NOT send transformed data into a third-party system under the guise of the Impersonated Account. Their audits would have no idea of the difference. It is why using a SSO with OAuth should be leveraged when posting data into a third party system and not a system id whenever possible, but that is a topic for another day.
I read a lot of other sources in thinking through this article that had some great ideas. One, Jamie’s Blog, read so similar to mine that I wanted to cite it (Great minds think alike and shows we’ve both had to solve this before). Jamie and others had some ideas I hadn’t considered, like having users use two-factor authentication when activating the impersonate mode or sending an alert to a Slack channel/email group so others know this is occurring, or going so far as to require another admin to approve the feature activation for their peer. The feature is somewhat common but comes with a lot of strings attached.
I hope I’ve done a decent job of sharing what I’ve researched and discovered on my own to help you with designing or considering this feature for your apps. If this is still too abstract and you think a video might help, let me know in the comments and I’ll see if I can get that done on a weekend sometime!