title: Impersonation Middleware in an Asp.Net Core Intranet app for Windows-Identity
body: <p>Before I explain my problem, here's our scenario:</p>
  <p><strong>Scenario</strong></p>
  <p>We write software only for our intranet Windows users (currently managed by local Active Directory but in future it is possible we migrate to Azure-AD).</p>
  <p>Up to yet there is an old monolithic Winforms app which communicates directly with the database using datasets. All requests to the database happens with <code>WindowsIdentity</code> (end-user context), so the database knows the end user.</p>
  <p>For future development we want to use a Web API for business logic. Only the web-app should access the database with Entity Framework. We wrote a Web API with ASP.NET Core (stateless) which is hosted in IIS. Because the web app runs in the app pools identity, we wrote a middleware which impersonates the context to the end-user (in order that the database access is working).</p>
  <p>While migrating to the web server both versions must be supported because we cannot migrate the whole application at once.</p>
  <p><strong>Problem</strong></p>
  <p>In debug environment the web server works fine (because there happens no impersonation) but on the live system sometimes the server crashes (not every time).</p>
  <p>The most frequent error we get is <code>FileLoadException</code> which cannot load dll's (mostly <code>System.Reflection</code>). Other times the whole server runs without errors return 200 OK but doesn't contain any http body.</p>
  <p>So it seems there is a problem. The documentation has a small hint to this:</p>
  <p><em><a href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.1#impersonation" rel="nofollow noreferrer">https://docs.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.1#impersonation</a></em>
  <a href="https://i.stack.imgur.com/fyElB.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/fyElB.png" alt="enter image description here" /></a></p>
  <p>So run the whole request in user context seems a bad idea but would could be the solution?</p>
  <p>Of course all we have to discard is the impersonation middleware. But how access the database?</p>
  <p><strong>Option #1</strong> : run each database call impersonated</p>
  <p><strong>Question</strong></p>
  <p>The official documentation says that code in the impersonation context is forbidden to run async things. We can do that, no problem. But I don't know if the Entity Framework runs async parts? If that would happen our app can crash again. Do you have any idea?</p>
  <p><strong>Question #2</strong></p>
  <p>If two requests come in asp would run two threads. Is it allowed to call impersonation at some time async on both reqeusts? The official documentation is not so clear.</p>
  <p><strong>Option #2</strong>: change our database so the end user is not required</p>
  <p><strong>Question</strong></p>
  <p>Some SQL triggers (and stored orocedures) use the user name for e.g. write the name into a table on insert. Of course we can change that behaviour so the EF Core write the name by hand.</p>
  <p>This problem is more a political one to find reasons why we should change the working solution away from <code>WindowsIdentity</code> to an <code>AppIdentity</code>. Do you ahve some good arguments?</p>
  <p><strong>Option #3</strong>: do you have any more ideas?</p>
  <p>I don't see more solutions, maybe you do?</p>
  <hr />
  <p>By the way. Here is the code of our impersonation middleware:</p>
  <pre class="lang-cs prettyprint-override"><code>public class ImpersonateMiddleware
  {
      private readonly RequestDelegate _next;
  
      public ImpersonateMiddleware(RequestDelegate next)
      {
          _next = next;
      }
  
      public async Task Invoke(HttpContext context)
      {
          var winIdent = context?.User?.Identity as WindowsIdentity;
  
          if (winIdent == null)
          {
              await _next.Invoke(context).ConfigureAwait(true);
          }
          else
          {
              await WindowsIdentity.RunImpersonated(winIdent.AccessToken, async () =&gt;
              {
                  await _next.Invoke(context)
                      .ConfigureAwait(true) 
                      ;
              }).ConfigureAwait(true);
          }
      }
  }
  </code></pre>