Would using Thread.CurrentPrincipal's claims in a referenced library that uses ConfigureAwait(false) pose any problems or will the flowing of ExecutionContext's logical call context take care of me there? (my reading and testing so far indicates that it will).
Example WebAPI Controller Action:
[CustomAuthorizeThatSetsCurrentUsersClaimsToThreadCurrentContextAndHttpContextCurrentUser]
public async Task<Order> Get(int orderId)
{
return await _orderBusinessLogicLibrary.LoadAsync(orderId); // defaults to .ConfigureAwait(true)
}
Example load functions from external, referenced library:
[ClaimsPrincipalPermission(
SecurityAction.Demand,
Operation="Read",
Resource="Orders")]
[ClaimsPrincipalPermission(
SecurityAction.Demand,
Operation="Read",
Resource="OrderItems")]
public async Task<Order> Load(int orderId)
{
var order = await _repository.LoadOrderAsync(orderId).ConfigureAwait(false);
// here's the key line.. assuming this lower-level function is also imposing
// security constraints in the same way this method does, would
// Thread.CurrentPrincipal still be correct inside the function below?
order.Items = await _repository.LoadOrderItemsAsync(orderId).ConfigureAwait(false);
return order;
}
Also, the answer can't be "well don't use ConfigureAwait(false) then!". That can cause other problems such as deadlock (Don't Block on Async Code).
Calling ConfigureAwait(false) after the task means that we do not care if the code after the await, runs on the captured context or not. In the output console, “True” will be printed since the synchronization context is not kept.
If the await task. ConfigureAwait(false) involves a task that's already completed by the time it's awaited (which is actually incredibly common), then the ConfigureAwait(false) will be meaningless, as the thread continues to execute code in the method after this and still in the same context that was there previously.
A situation to use ConfigureAwait(true) is when performing await in a lock, or using any other context/thread specific resources. This requires a synchronization context, which you will have to create, unless you are using Windows Forms or WPF, which automatically create a UI synchronization context.
ConfigureAwait(continueOnCapturedContext: false) is used to avoid forcing the callback to be invoked on the original context or scheduler. This has a few benefits: Improving performance.
From my tests, it appears that Thread.CurrentPrincipal
will flow correctly, even if you use ConfigureAwait(false)
. The following WebAPI code sets the principal and then blocks on an async
call, forcing another thread to resume the async
method. That other thread does inherit the correct principal.
private async Task<string> Async()
{
await Task.Delay(1000).ConfigureAwait(false);
return "Thread " + Thread.CurrentThread.ManagedThreadId + ": " + Thread.CurrentPrincipal.Identity.Name + "\n";
}
public string Get(int id)
{
var user = new ClaimsPrincipal(new ClaimsIdentity(
new[]
{
new Claim(ClaimTypes.Name, "Bob"),
}
));
HttpContext.Current.User = user;
Thread.CurrentPrincipal = user;
var ret = "Thread " + Thread.CurrentThread.ManagedThreadId + ": " + Thread.CurrentPrincipal.Identity.Name + "\n";
ret += Async().Result;
return ret;
}
When I run this code on a new instance of IISExpress, I get:
"Thread 7: Bob\nThread 6: Bob\n"
However, I should point out that using ConfigureAwait(false)
to avoid deadlock is not recommended. This is especially true on ASP.NET. If at all possible, use ConfigureAwait(false)
and also use async
all the way. Note that WebAPI is a fully-async
stack and you should be able to do this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With