Before I explain my problem, here's our scenario:
Scenario
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).
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 WindowsIdentity
(end-user context), so the database knows the end user.
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).
While migrating to the web server both versions must be supported because we cannot migrate the whole application at once.
Problem
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).
The most frequent error we get is FileLoadException
which cannot load dll's (mostly System.Reflection
). Other times the whole server runs without errors return 200 OK but doesn't contain any http body.
So it seems there is a problem. The documentation has a small hint to this:
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.1#impersonation
So run the whole request in user context seems a bad idea but would could be the solution?
Of course all we have to discard is the impersonation middleware. But how access the database?
Option #1 : run each database call impersonated
Question
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?
Question #2
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.
Option #2: change our database so the end user is not required
Question
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.
This problem is more a political one to find reasons why we should change the working solution away from WindowsIdentity
to an AppIdentity
. Do you ahve some good arguments?
Option #3: do you have any more ideas?
I don't see more solutions, maybe you do?
By the way. Here is the code of our impersonation middleware:
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 () =>
{
await _next.Invoke(context)
.ConfigureAwait(true)
;
}).ConfigureAwait(true);
}
}
}