How do you do Impersonation in .NET?

ashwnacharya picture ashwnacharya · Sep 24, 2008 · Viewed 251.8k times · Source

Is there a simple out of the box way to impersonate a user in .NET?

So far I've been using this class from code project for all my impersonation requirements.

Is there a better way to do it by using .NET Framework?

I have a user credential set, (username, password, domain name) which represents the identity I need to impersonate.

Answer

Matt Johnson-Pint picture Matt Johnson-Pint · Aug 30, 2011

"Impersonation" in the .NET space generally means running code under a specific user account. It is a somewhat separate concept than getting access to that user account via a username and password, although these two ideas pair together frequently. I will describe them both, and then explain how to use my SimpleImpersonation library, which uses them internally.

Impersonation

The APIs for impersonation are provided in .NET via the System.Security.Principal namespace:

  • Newer code (.NET 4.6+, .NET Core, etc.) should generally use WindowsIdentity.RunImpersonated, which accepts a handle to the token of the user account, and then either an Action or Func<T> for the code to execute.

    WindowsIdentity.RunImpersonated(tokenHandle, () =>
    {
        // do whatever you want as this user.
    });
    

    or

    var result = WindowsIdentity.RunImpersonated(tokenHandle, () =>
    {
        // do whatever you want as this user.
        return result;
    });
    
  • Older code used the WindowsIdentity.Impersonate method to retrieve a WindowsImpersonationContext object. This object implements IDisposable, so generally should be called from a using block.

    using (WindowsImpersonationContext context = WindowsIdentity.Impersonate(tokenHandle))
    {
        // do whatever you want as this user.
    }
    

    While this API still exists in .NET Framework, it should generally be avoided, and is not available in .NET Core or .NET Standard.

Accessing the User Account

The API for using a username and password to gain access to a user account in Windows is LogonUser - which is a Win32 native API. There is not currently a built-in .NET API for calling it, so one must resort to P/Invoke.

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);

This is the basic call definition, however there is a lot more to consider to actually using it in production:

  • Obtaining a handle with the "safe" access pattern.
  • Closing the native handles appropriately
  • Code access security (CAS) trust levels (in .NET Framework only)
  • Passing SecureString when you can collect one safely via user keystrokes.

The amount of code to write to illustrate all of this is beyond what should be in a StackOverflow answer, IMHO.

A Combined and Easier Approach

Instead of writing all of this yourself, consider using my SimpleImpersonation library, which combines impersonation and user access into a single API. It works well in both modern and older code bases, with the same simple API:

var credentials = new UserCredentials(domain, username, password);
Impersonation.RunAsUser(credentials, logonType, () =>
{
    // do whatever you want as this user.
}); 

or

var credentials = new UserCredentials(domain, username, password);
var result = Impersonation.RunAsUser(credentials, logonType, () =>
{
    // do whatever you want as this user.
    return something;
});

Note that it is very similar to the WindowsIdentity.RunImpersonated API, but doesn't require you know anything about token handles.

This is the API as of version 3.0.0. See the project readme for more details. Also note that a previous version of the library used an API with the IDisposable pattern, similar to WindowsIdentity.Impersonate. The newer version is much safer, and both are still used internally.