The code below was added to a freshly created Visual Studio 2012 .NET 4.5 WebAPI project.
I'm trying to assign both HttpContext.Current.User
and Thread.CurrentPrincipal
in an asynchronous method. The assignment of Thread.CurrentPrincipal
flows incorrectly unless an await Task.Yield();
(or anything else asynchronous) is executed (passing true
to AuthenticateAsync()
will result in success).
Why is that?
using System.Security.Principal;
using System.Threading.Tasks;
using System.Web.Http;
namespace ExampleWebApi.Controllers
{
public class ValuesController : ApiController
{
public async Task GetAsync()
{
await AuthenticateAsync(false);
if (!(User is MyPrincipal))
{
throw new System.Exception("User is incorrect type.");
}
}
private static async Task AuthenticateAsync(bool yield)
{
if (yield)
{
// Why is this required?
await Task.Yield();
}
var principal = new MyPrincipal();
System.Web.HttpContext.Current.User = principal;
System.Threading.Thread.CurrentPrincipal = principal;
}
class MyPrincipal : GenericPrincipal
{
public MyPrincipal()
: base(new GenericIdentity("<name>"), new string[] {})
{
}
}
}
}
Notes:
- The
await Task.Yield();
can appear anywhere inAuthenticateAsync()
or it can be moved intoGetAsync()
after the call toAuthenticateAsync()
and it will still succeed. ApiController.User
returnsThread.CurrentPrincipal
.HttpContext.Current.User
always flows correctly, even withoutawait Task.Yield()
.Web.config
includes<httpRuntime targetFramework="4.5"/>
which impliesUseTaskFriendlySynchronizationContext
.- I asked a similar question a couple days ago, but did not realize that example was only succeeding because
Task.Delay(1000)
was present.
await Task.Yield()
is skipped,Thread.CurrentPrincipal
reverts back to what it was before callingawait AuthenticateAsync()
. SinceThread.CurrentPrincipal
is no longer aMyPrincipal
, the exception is thrown. – Gazehound