1

I have a WebApi controller that initially authenticates as a specific WebApi user. Subsequent accesses to the web api will pass a user that operations should be performed as, without having to actually authenticate as that user.

I have some services/managers that perform functions as those proper users as part of an MVC project. I now want to use those services and managers with the WebApi project, but I don't want to have to pass the user around.

I'm hoping I can temporarily change the identity of the Web Api call after the user passed in the Web Api call has been validated, but I want to make sure that when the call is complete, the cookie returned is for the validation of the WebApi user, not the end user that is represented as a part of the call.

My question is, what can I do to temporarily change the identity to the validated user in the call, and then change back to the web api identity?

Reuben
  • 4,136
  • 2
  • 48
  • 57
  • Found a nice article [http://tech.trailmax.info/2014/06/user-impersonation-with-asp-net-identity-2/], and related SO Q [http://stackoverflow.com/questions/24161782/how-do-i-use-asp-net-identity-2-0-to-allow-a-user-to-impersonate-another-user] that gets me part way. At this point, I'm thinking a disposable object that does validation, impersonation and a restore might be in order. – Reuben Mar 05 '15 at 22:18

1 Answers1

0

Loosely using code from the links in the post, I created a IDisposable object that would temporarily change the identity.

Usage is:

try
{
    using(new Impersonate(userManager, userName))
    {
       /* do your stuff as userName */
    }
}
catch (ImpersonateException) {}

The Impersonate class is as follows:

public class Impersonate : IDisposable
{
    private UserManager<ApplicationUser> userManager;

    public Impersonate(UserManager<ApplicationUser> userManager, string userName) 
    {
        this.userManager = userManager;

        if (ValidateUser(userName))
        {
            this.ImpersonateUser(userName);
        }
        else
        {
            throw new ImpersonateException("Current user does not have permissions to impersonate user");
        }
    }

    private bool ValidateUser(string userName) 
    {
        /* validate that the current user can impersonate userName */
    }

    public void Dispose()
    {
       this.RevertImpersonation();
    }

    private void ImpersonateUser(string userName) 
    {
       var context = HttpContext.Current;
       var originalUsername = context.User.Identity.Name;

       var impersonatedUser = this.userManager.FindByName(userName);

       var impersonatedIdentity = impersonatedUser.GenerateUserIdentity(this.userManager, "Impersonation");
       impersonatedIdentity.AddClaim(new Claim("UserImpersonation", "true"));
       impersonatedIdentity.AddClaim(new Claim("OriginalUsername", originalUsername));

       var impersonatedPrincipal = new ClaimsPrincipal(impersonatedIdentity);

       context.User = impersonatedPrincipal;
        Thread.CurrentPrincipal = impersonatedPrincipal;
    }

    private void RevertImpersonation()
    {
        var context = HttpContext.Current;

        if (!ClaimsPrincipal.Current.IsImpersonating())
        {
            throw new ImpersonationException("Unable to remove impersonation because there is no impersonation");
        }

        var originalUsername = ClaimsPrincipal.Current.GetOriginalUsername();

        var originalUser = this.userManager.FindByName(originalUsername);

        var originalIdentity = originalUser.GenerateUserIdentity(this.userManager);
        var originalPrincipal = new ClaimsPrincipal(originalIdentity);

        context.User = originalPrincipal;
        Thread.CurrentPrincipal = originalPrincipal;
    }
}

This differs the linked code in that it only sets the identity temporarily, so doing having to SignIn/SignOut is not required.

Also, since the bulk of the work is done in the constructor, I had to remove the Async aspects that the linked code uses. There might be a way around it, but I'm not experienced enough with Async, or patient enough to bother.

Reuben
  • 4,136
  • 2
  • 48
  • 57