Using ASP.NET Web API, my ExecutionContext isn't flowing in async actions
I don't have all the answers, but I can help fill in some blanks and guess at the problem.
By default, the ASP.NET SynchronizationContext
will flow, but the way it flows identity is a bit weird. It actually flows HttpContext.Current.User
and then sets Thread.CurrentPrincipal
to that. So if you just set Thread.CurrentPrincipal
, you won't see it flow correctly.
In fact, you'll see the following behavior:
- From the time
Thread.CurrentPrincipal
is set on a thread, that thread will have that same principal until it re-enters an ASP.NET context. - When any thread enters the ASP.NET context,
Thread.CurrentPrincipal
is cleared (because it's set toHttpContext.Current.User
). - When a thread is used outside the ASP.NET context, it just retains whatever
Thread.CurrentPrincipal
happened to be set on it.
Applying this to your original code and output:
- The first 3 are all reported synchronously from thread 63 after its
CurrentPrincipal
was explicitly set, so they all have the expected value. - Thread 77 is used to resume the
async
method, thus entering the ASP.NET context and clearing anyCurrentPrincipal
it may have had. - Thread 63 is used for
ProcessResponse
. It re-enters the ASP.NET context, clearing itsThread.CurrentPrincipal
. - Thread 65 is the interesting one. It is running outside the ASP.NET context (in a
ContinueWith
without a scheduler), so it just retains whateverCurrentPrincipal
it happened to have before. I assume that itsCurrentPrincipal
is just left over from an earlier test run.
The updated code changes PostFile
to run its second portion outside the ASP.NET context. So it picks up thread 65, which just happens to have CurrentPrincipal
set. Since it's outside the ASP.NET context, CurrentPrincipal
isn't cleared.
So, it looks to me like ExecutionContext
is flowing fine. I'm sure Microsoft has tested ExecutionContext
flow out the wazoo; otherwise every ASP.NET app in the world would have a serious security flaw. It's important to note that in this code Thread.CurrentPrincipal
just refers to the current user's claims and does not represent actual impersonation.
If my guesses are correct, then the fix is quite simple: in SendAsync
, change this line:
Thread.CurrentPrincipal = new ClaimsPrincipal(new ClaimsPrincipal(new ClaimsIdentity(new[]{ new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "dgdev") }, "myauthisthebest")));
to this:
HttpContext.Current.User = new ClaimsPrincipal(new ClaimsPrincipal(new ClaimsIdentity(new[]{ new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "dgdev") }, "myauthisthebest")));
Thread.CurrentPrincipal = HttpContext.Current.User;