I've found a solution to this problem.
Basically I add claim with admin username, if this claim exists, I know that impersonation is happening. When admin wants to stop impersonation, system retrieves original username for the claims, deletes old impersonated-cookie and creates a new cookie for the admin:
[AuthenticateAdmin] // <- make sure this endpoint is only available to admins public async Task ImpersonateUserAsync(string userName) { var context = HttpContext.Current; var originalUsername = context.User.Identity.Name; var impersonatedUser = await userManager.FindByNameAsync(userName); var impersonatedIdentity = await userManager.CreateIdentityAsync(impersonatedUser, DefaultAuthenticationTypes.ApplicationCookie); impersonatedIdentity.AddClaim(new Claim("UserImpersonation", "true")); impersonatedIdentity.AddClaim(new Claim("OriginalUsername", originalUsername)); var authenticationManager = context.GetOwinContext().Authentication; authenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie); authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = false }, impersonatedIdentity); }
More information is in my blog-post: User impersonation with ASP.Net Identity 2.
User Impersonation in Asp.Net Core
Upd July 2017: this topic is quite popular, so I've looked into user impersonation in Core and principles are very similar with updated API. Here is how to impersonate:
[Authorize(Roles = "Admin")] // <-- Make sure only admins can access this public async Task<IActionResult> ImpersonateUser(String userId) { var currentUserId = User.GetUserId(); var impersonatedUser = await _userManager.FindByIdAsync(userId); var userPrincipal = await _signInManager.CreateUserPrincipalAsync(impersonatedUser); userPrincipal.Identities.First().AddClaim(new Claim("OriginalUserId", currentUserId)); userPrincipal.Identities.First().AddClaim(new Claim("IsImpersonating", "true")); // sign out the current user await _signInManager.SignOutAsync(); // If you use asp.net core 1.0 await HttpContext.Authentication.SignInAsync(cookieOptions.ApplicationCookieAuthenticationScheme, userPrincipal); // If you use asp.net core 2.0 (the line above is deprecated) await HttpContext.SignInAsync(cookieOptions.ApplicationCookieAuthenticationScheme, userPrincipal); return RedirectToAction("Index", "Home"); }
This is how to stop impersonation:
[Authorize(Roles = "Admin")] // <-- Make sure only admins can access this public async Task<IActionResult> StopImpersonation() { if (!User.IsImpersonating()) { throw new Exception("You are not impersonating now. Can't stop impersonation"); } var originalUserId = User.FindFirst("OriginalUserId").Value; var originalUser = await _userManager.FindByIdAsync(originalUserId); await _signInManager.SignOutAsync(); await _signInManager.SignInAsync(originalUser, isPersistent: true); return RedirectToAction("Index", "Home"); }
Full explanation in my blog: http://tech.trailmax.info/2017/07/user-impersonation-in-asp-net-core/ Full code sample on GitHub: https://github.com/trailmax/AspNetCoreImpersonation