A screenshot of ASP.NET forms authenticated user impersonation in action: The user 'Bob' is currently impersonating the user 'Alice'.

Introduction

Part of building a complete web application involves building a decent administration and support interface. A very important part of a good support interface is the ability of support users to login as an arbitrary client user. At the company I work for, they have several people sitting next to their phone at the office using this functionality every day. The company offers some kind of software as a service, their (trusted) support employees handle support calls from client users who have trouble getting stuff done in the application. If the issue isn't resolved directly, the support users use the functionality described in this article to log in as the troubled user 'and be able to see what the actual user sees'. The support users then either talk the client through the procedure for whatever they want done, or do it themselves directly. Half of the time, the support users at the company I work for even get called and just asked to do something for the client in the client's account.

For such operations to run smooth, the support users need to be able to jump from the administration and support interface directly into the client accounts. We can't go about resetting password and stuff; when they get a client on the phone, they just should be able to do what they have to do as quickly and efficient as possible.

While logging an ASP.NET user in as another user is quite easy; in fact, a call to FormsAuthentication.SetAuthCookie() will do the job; I, however, needed a way to really streamline this kind of functionality. I found it to be quite desirable for the support user to be able to jump right back to his or her support user account after impersonating the other user, without the hassle of having to really login as the support user again. Since I was unable to find any existing solutions, I developed the UserImpersonation class and the LoginUserImpersonation control presented in this article to tackle exactly that problem.

In this article, I will first briefly describe the requirements for using the UserImpersonation class and how to use it. After that, I will go into more detail on how I implemented user impersonation, and the motivation behind the decisions I made during the development.

Note: This article is not about what is usually referred to as 'Windows user impersonation'. This article only deals with ASP.NET forms authenticated users.

Using the UserImpersonation class

Requirements

In order to be able to make use of the UserImpersonation class, the following conditions and requirements apply:

One must make use of cookie based FormsAuthentication in order to be able to make use of the UserImpersonation class.

Cookie encryption is highly recommended while making use of the UserImpersonation class.

Only tested on .NET 2.0. The UserImpersonation class should thus work on .NET 2.0, 3.0, and 3.5.

You, at the moment, do not make use of the FormsAuthenticationTicket.UserData property in your ASP.NET application/website.

Reference

Using the UserImpersonation class is petty straightforward. The UserImpersonation is located in the System.Web.Security namespace, and has the following members:

Quick start

In order to start using the user impersonation functionality right away in your web application, follow the steps below:

Download the FormsAuthenticationUserImpersonation precompiled binary into the /Bin folder of your ASP.NET application/website. This operation is equal to 'adding a reference' to the FormsAuthenticationUserImpersonation precompiled binary for your ASP.NET application/website.

Register the FormsAuthenticationUserImpersonation assembly on the page you wish to use the LoginUserImpersonation control. This will likely be your master page. Registering an assembly is done using the <%@ Page %> directive as follows:

Add the LoginUserImpersonation control at the desired location on your ASP.NET page. Typically, you'll want to place this next to the login/logout button. The LoginUserImpersonation control will be rendered as a link button with the text 'Return to {UserName}':

Add the following code to your code where you want to actually start the user impersonation, where strUserName contains the name of the user you want to be logged in as. It is advised to redirect the user right after user impersonation is started, in order to make sure the changes take effect:

Included example and source code

The included example website uses a rudimentary read-only MembershipProvider providing two users: Bob and Alice. For both of the users, the password is test.

The example website features a home page with a button allowing the logged in user to impersonate Alice, a login page, and a page only accessible by Alice.

The UserImpersonation class and LoginUserImpersonation control were written in C#. The source code is documented, and should be fairly easy to understand for any programmer at least being lightly acquainted with ASP.NET and C#.

Development

I will now discuss some interesting points I came across while developing this solution.

Design goals

Keep the user impersonation logic as close as possible to the existing user authentication logic.

Allow the user to easily return to his or her original account after impersonating another user without the hassle of having to login onto his or her own account again.

Keeping track of the original user

The Problem

As I already mentioned in the introduction, having a user to login as another user is actually quite simple. Just logging in the support user as the troubled client user will, however, result in the support user having to login into his or her account after he or she wants to end impersonating the other user. While this is no big deal for some dude administrating some small web application once a month, this will get really annoying for people working at the office helping clients all day using the application I am developing at the moment.

In short, I needed some way to log an user in as another user while at the same time keep track of the user he or she was previously fully authenticated for. This all needed to be done in a secure manner, because I wanted to give the user the ability to return to his or her original user account with a single mouse click, no re-entering passwords involved.

Authentication cookies

One secure and obvious way to handle this problem would be to keep track of the previous user by storing this information is the Session property bag. This, however, would require sessions to be enabled on every single page, or an HTTP handler reachable by authenticated users, and would require extra tracking code because the session does not necessarily expire when the user authentication cookie expires. I, therefore, decided it was not optimal to store the previous user information in the session.

The most logical place to store this information would be the authentication cookie itself, wouldn't it? It's securely encoded, and all information expires when the cookie expires (duh). The authentication cookie, however, is a little less accessible than the Session property bag. After some sniffing around on MSDN, I noted the FormsAuthenticationTicket class, which in a way represents the decoded version of the authentication cookie actually exposed as a property which allows one to include custom data with the cookie: FormsAuthenticationTicket.UserData.

Piggyback on the authentication cookie

When stating user impersonation, I use the following code to create a new authentication ticket for the user to be impersonated, piggybacking the data which describes the previous user as shown in the following code, which is taken from the UserImpersionation.ImpersonateUser function:

First, the user name of the current user and an optional return URL are joined using a simple serialization function. Next, a new authentication cookie is created for the user to be impersonated and stored in the authCookie variable. Since the FormsAuthentication class directly produces an encrypted authentication cookie, this cookie is then decoded to a FormsAuthenticationTicket class stored in the authTicket variable.

Because all properties of the FormsAuthenticationTicket class are read-only, a new FormsAuthenticationTicket class instance is created effectively, cloning the previously obtained FormsAuthenticationTicket class instance, but also carrying our serialized data. This new FormsAuthenticationTicket class instance is then encrypted into our new authentication cookie, which sequentially is added to the response headers.

Retrieving the previous user name

After our new impersonation enabled authentication cookie has been set, it's quite straightforward to obtain the previous user name as shown in the following code, taken from the UserImpersionation.PrevUserName.get property:

First, the User.Identity property which exposes an IIdentity interface by default is casted to an instance of the FormsIdentity class. Note that the User.Identity property only contains a FormsIdentity class instance when the form authentication is used. Since the FormsIdentity class exposes the FormsAuthenticationTicket directly, the previous user name is easily obtained by deserializing the contents of the UserData property.

Redirecting the user on deimpersonation

As you may have noticed while reading the article, an optional redirect URL is stored in the authentication cookie. The UserImpersonation.Deimpersonate() method redirects the user to this location after impersonation is reverted.

In the application I am developing at the moment and use this user impersonation, the support users can impersonate some client user by clicking a button placed on a page describing the details of the concerned client user. I want the whole user impersonation experience to be like starting a new session, and ending thes session cleanly by arriving at the point where the session was started.

Performance

I suspect performance to be hardly affected by using the UserImpersonation class. Yes, the previous user and redirect URL are included in the authentication cookie, but I'm having a hard time believing those 100 extra bytes will kill your performance even the slightest bit.

When considering performance extremes: I suspect this solution to be way more efficient than using the session for storing the previous user name and redirect URL. Assuming your application is running in a web farm, fetching the session will likely require an extra roundtrip toward the database server, taking anywhere between 20 and 100 ms. While I haven't tested this, I expect decrypting the authentication cookie will not even take a single millisecond.

Conclusion

I think I succeeded quite well implementing ASP.NET user impersonation. It surprised me I was unable to find any other examples of the functionality described in this article, since it seems this kind of stuff is likely to be present in most enterprise level applications, even while this whole user impersonation trick was not the most difficult thing to implement.

In this way, I want to contribute something back to the whole Open Source community which basically helped me from learning to program in the first place to solve many problems I now encounter as a professional programmer. As always, feel free to comment, did I miss something, is stuff not working, got a better solution? Drop a comment!

Thanks. Yes, while this article is fairly old now I would definitely recommend any one using this to clearly register administrator access in the audit log. In that way you can prevent administrators from 'sneaking' in with the wrong intentions.

The problem here is not "I want to impersonnate the user", the problem is "I am an administrator and i'd like to see this user's interface" (no one changes its identity), so I think the solutions is more on the authorization side than on the authentication side. But your solution is easier to implement I guess (because this.User on your controller will always be the customer).

I gave you a 3 because you can't impersonnate multiple user (on different tab), and that could also be usefull on a call center.

Yes, you are right. Another way would be to make sure than an administrator is authorized to access the client side of the application as well. You still however do need to write some custom code if you want the administrator to use the regular client interface as your application needs to know as which client the administrator is accessing the application. Using my approach is a quick one to achieve this in a simple manner.

The real and proper solution is to make a very complete administrator back end. But hey, we are all short on time.

Hi Christ,
This looks like exactly what I need. Too bad I didn't see this a year ago!

By leaving the impersonation info around in the cookie, is it possible for someone with that cookie to spoof the administrator's login? I would welcome your thoughts on that.

Also, you mentioned that it's up to the developer to decide who gets to impersonate whom. Where in the code do you suggest is the most logical place to do that? I have a situation where users at a given level are only allowed to impersonate users who are at a lower level.

Moreover, I may need to restrict the capabilities of an impersonated user. For example, an admin might be able to execute a payment on behalf of an impersonated customer, but nevertheless be forbidden from seeing the payment card info. I would be interested in your thoughts on that as well.

As far as I can see there is no security issue because the complete authentication cookie is encrypted using the configured machine key. As long as your code does not expose UserData and you machine key is not compromised, no one should be able to touch it.

Has anybody got this working with MVC? It seems like a great solution, but I'm not having any luck getting the control to render on the page. (In design view, I get "Error Creating Control - LoginUserImpersonation...No HttpContext available. Unable to complete operation.")

It won't work in the designer because the designer is just a simulated view of a page/control. When viewing through the designer the control is not rendered in the context of a web server request, thus there is no HttpContext available.

I didn't use the control either (all it does is render a link anyway), but I've modified my UserImpersonation.cs as follows to make it a little easier to work with in an MVC app:

I've always just used FormsAuthentication.RedirectFromLoginPage(UserName, True) to do my impersonating. The user has to then log out and then log back into the admin account. I like how yours tracks the user so they can just log back in with the click of a button.

And why would that be? When I search for 'ASP.NET user impersonation' for example, all I get is stuff about impersonating windows users for accessing network shares and stuff. Well perhaps I didn't search for the completely correct term, English is not my native language. But as you can see may English surely isn't that bad so please, at least clearly point out what I did wrong when saying so.

All right, we seem to have some misunderstanding here. Of course it's in the real world undesirable for an actual systems administrator for take for example a webmail application to be logging in as random users and sniffing around.

But I actually have several people sitting next to their phone at our office using this functionality every day. My company offers some kind of software as a service, our (trusted) support employees handle support calls from client users who have trouble getting stuff done in our application. If the issue isn't resolved directly, our support users user the in this article described functionality to log in as the troubled user 'and be able to see what the actual user sees'. Out support users then either talk the client trough the procedure for whatever they want done or do it themselves directly. Half of the time out support users even just get called and asked to perform some operations for the clients. I presume you let your support users reset passwords every time some client needs assistance?

And who said any support/administrative user gains access to literally any user he of she likes using my code? If you would have taken a look at the code you would have seen it's completely up to the developer to specify which user to impersonate. My support users using this very piece of code certainly are not able to login onto client accounts of clients in different departments, heck they probably don't even know they exists.

Also if you do not have any problem with my code, only with the application of it, why vote a 2? This article surely does not provide any means of implementing the in your opinion incorrect application directly, only the described functionality. More than two third of the article is about how this authentication cookie stuff actually works. Don't you think that'll actually be interesting for less advanced users to read?

Well perhaps I should change the introduction of this article to make more clear this functionality isn't really targeted at administrator users, but more toward the support users.

There are not only administrators searching articles here. Maybe you are standing at the administrator position, you can share your point of view of course but please don't assume every administrator doesn't need every thing you don't need. Moreover, developers are here too. And this article is good and useful. At least I think it's very useful when testing functionality of different roles of a website. I vote 5.