Question

At the beginning of each request, I set the Thread.CurrentPrincipal in my AuthorizationFilter to the authenticated user with custom claims. When I try to log errors thrown in a controller action in my ExceptionFilter, the principal accessed from within the ExceptionFilter isn't the same as the one I set in my AuthorizationFilter. In fact, the principal I get in the ExceptionFilter is the principal that existed on the thread before I changed it in my AuthorizationFilter. After browsing the source code of Web API, I think I found the problem which is related to the execution context of tasks (see the ApiController class, ExecuteAsync method). The sample code below is a simple way to reproduce the problem:

class Program
{
    static void Main(string[] args)
    {
        SetIdentity("originIdentity");

        Func<Task> controllerAction = () =>
        {
            Console.WriteLine("Identity is correct in controller: " + Thread.CurrentPrincipal.Identity.Name);

            return Task.FromResult("result");
        };

        Func<Task> authFilter = async () =>
        {
            SetIdentity("newIdentity");
            Console.WriteLine("Set identity to " + Thread.CurrentPrincipal.Identity.Name);

            await controllerAction();
        };

        Func<Task> exceptionFilter = async () =>
        {
            Console.WriteLine("Original identity: " + Thread.CurrentPrincipal.Identity.Name);

            //there would be a try block around this so we can catch exceptions
            await authFilter();

            Console.WriteLine("Identity is what it was before: " + Thread.CurrentPrincipal.Identity.Name);
        };

        exceptionFilter().Wait();

        Console.Read();
    }

    private static void SetIdentity(string originidentity)
    {
        var originIdentity = new ClaimsIdentity("HeaderAuthentication");
        originIdentity.AddClaim(new Claim(ClaimTypes.Name, originidentity));
        Thread.CurrentPrincipal = new ClaimsPrincipal(originIdentity);
    }
}

And output:

Original identity: originIdentity
Set identity to newIdentity
Identity is correct in controller: newIdentity
Identity is what it was before: originIdentity

My questions are:

  1. Am I seeing what I think I'm seeing?
  2. Is this a bug or by design in Web API? It makes sense the TPL behaves this way but it's hard to imagine this is intended behavior in Web API
  3. What is the recommended/clean way to get around this in Web API?
Was it helpful?

Solution

Thread.CurrentPrincipal has some problems in certain threading/parallelism situations.

That's why it is not used anymore in Web API v2. Use the GetRequestContext() extension method on HttpRequestMessage to get to the current user / or setting it

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top