views:

67

answers:

2

In our application, we have a need for a user to "impersonate" a different user. Think of it as a hierarchy -- Bob is above Frank in a hierarchy. Bob is logged in, and he needs to do things in the system for a short time as Frank. So, we have given Bob a list of users that report to him, and an impersonate link. He clicks on this link, and, behind the scenes, I log Bob out, and log in as Frank. I also set a session variable that tells me that really Bob is they guy who is the user. Also, Bob (acting as Frank now) has a nice little link at the top of every page that says "Stop Impersonation."

In addition, when Bob is impersonating Frank, Bob is restricted from doing some things, like changing Frank's password.

This was working great, until we encountered a situation where, if the session (I think -- getting confused here) gets destroyed (such as when I copy up new code and dlls to the live site), then when Bob clicks on "Stop Impersonation" he gets redirected to the default page, and is still logged in as Frank, but without the Impersonation session variable. So, now Bob really is logged in as Frank, and can change Frank's password (among other things).

How is it that a session variable (Impersonation) gets destroyed, but I guess the session is still hanging around, because it doesn't make the user log in again?

This is a somewhat serious bug for how our system works (bug in our code, I'm sure, not in .Net). Does anyone have any suggestions for a solution for this?

We are using ASP.Net c#, aspnet membership services, .net 3.5, forms auth...not sure what else you need to know.

EDIT: Updated information. Looks like when "something" happens, for instance, when I recompile some dlls and copy them to the webserver, the session gets dumped. Or, rather, the variables in the session get dumped. The session id stays the same. I do get to check for Session.IsNewSession and it returns true, even though the id is the same as it was before.

Just like Utaal mentioned, Membership Services is separate from Session, so it's forms auth token is still hanging around in the browser, but my session variable telling me that that isn't really the user who is controlling the browser isn't there anymore.

EDIT: Sky, here is what I'm doing to authenticate a user. I can't figure out where I would insert a ticket into this flow:

if (Membership.ValidateUser(txtUserName.Text, txtPassword.Text))
    FormsAuthentication.SetAuthCookie(txtUserName.Text, false);

So, where can I slip in a ticket object and set my own information?

+2  A: 

I think your problem is due to the fact that Forms Authentication and Session are two different things and are not interconnected: both of them (usually) use cookies but Forms Authentication stores the encrypted logged-in user directly in the cookie while Session stores information in-process (even if you can change this behaviour) and uses a cookie with a session identifier to retrieve it.

So, when your session information gets lost (or session expires) it isn't really still hanging around (except for the invalid session cookie on the user's pc). On the other hand the Forms Authentication cookie is still valid (ASP.NET decrypts it and authenticates the user for the request).

A possible solution is to detect the creation of a new session (using HttpSessionState.IsNewSession MSDN) and sign out the user (using FormsAuthentication). You can then redirect user to login page.

Utaal
Thanks Utaal. I'll dig into this a bit tonight when I have time to experiment.
Matt Dawdy
+2  A: 

Matt, Use the UserData slot on the forms ticket to store the impersonation information. That is what it is for.

Then your info will not get lost with the session.

If you would like a simple example of creating your own ticket, amongst other things, check this. You may want to focus on the login page and the tickethelper class.

Sky Sanders
On your suggestion I tried this, but you can't use your own validation and set the auth cookie yourself and use this. "ticket" is read only at that point. Thanks for the suggestion, though.
Matt Dawdy
@matt, I am not sure I follow you. It is my experience that the ticket is what you say it is and most especially when you perform your own validation you have a perfect opportunity to construct the ticket any way you want before you set it. When you say "at that point" exactly when/where are you referring? I only ask because I would like to see you get an elegant solution to your problem. I have made auth tickets do stupid human tricks, buy me lunch and wash the dishes before I set them out. Just sayin... lol.
Sky Sanders
Sky, then I'm confused. See my edited post so I can show you the code I'm using. I would REALLY like to use the ticket to do this, but I think I'm missing something huge here.
Matt Dawdy
Thanks Sky. I'll dig through the code you linked to (just saw that...maybe I missed it last night -- it has been a long 6 months, with 1 more to go).
Matt Dawdy
Sky, that worked great. I've got a couple of issues that I still need to solve (something about Identity being a GenericIdentity object instead of FormsIdentity object) but I'm 95% there. Thanks for sticking with me.
Matt Dawdy
@Matt Dawdy, glad I could help. If the id is generic it means that the user is not logged id, e.g. no ticket found by FormsAuthenticationModule.
Sky Sanders