runtime: Thread.CurrentPrincipal does not flow with ExecutionContext

We have a problem with System.Threading.ExecutionContext and System.Threading.Thread.CurrentPrincipal on .NET Core 2.1.

We want to implement a custom asynchronous point. According to docs.microsoft.com System.Threading.ExecutionContext is the way to go:

The ExecutionContext class provides the functionality for user code to capture and transfer this context across user-defined asynchronous points.

Test:

using System;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;

class Program
{
	static void Main()
	{
		var principal = new ClaimsPrincipal();

		Thread.CurrentPrincipal = principal;
		var context = ExecutionContext.Capture();
		Thread.CurrentPrincipal = null;

		ExecutionContext.Run
		(
			context,
			state =>
			{
				Console.WriteLine("Thread.CurrentPrincipal: {0}", Thread.CurrentPrincipal);
			},
			null
		);
	}
}

Using .NET Framework 4.7.1 I get: Thread.CurrentPrincipal: System.Security.Claims.ClaimsPrincipal Wich is the expected behavior, according to the documentation

The ExecutionContext class provides a single container for all information relevant to a logical thread of execution. This includes security context, call context, and synchronization context.

Using .NET Core 2.1 I get: Thread.CurrentPrincipal:

I tested the behavior with the runtime provided asynchronoues point “await” and “Task.Run”. In both cases it worked as expected.

Is this behavioral change intended?

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 32 (26 by maintainers)

Most upvoted comments

Both me and @stephentoub have given green light to fixing Thread.CurrentPrincipal to be more compatible with .NET Framework in the discussion we had about this.

If the CurrentPrincipal was part of ExecutionContext on desktop, it makes sense to flow it in .NET Core as well for better compatibility.

It should be done lazily - create the AsyncLocal only once somebody actually sets the principal.

You are right, my tests were wrong. Await and Task.Run also doen’t work on .NET Core.

This is a big issue for us, and it doesn’t answer the question if it is a bug or a feature. Other contextual data like CurrentCulture and CurrentUICulture flow as they were ported to AsyncLocal.

ASP.NET Core is always going to consider anything .Current (including ClaimsPrincipal.Current) as an anti-pattern. We purposely avoided it, and we won’t be setting this either 😃

behaving differently on .net core and .net framework is worth fixing.

I asked because “it depends”, after my recent experience 😄 https://github.com/dotnet/corefx/pull/33645#issuecomment-454100489 https://github.com/dotnet/corefx/issues/17164#issuecomment-454143649

This looks like a pretty straightforward compat bug fix to me. I do not think this needs any special review.

default on full framework is empty GenericPrincipal but is null on .net core

The default principal policy on .NET Core is PrincipalPolicy.NoPrincipal. It is intentional. It is one-liner for the apps to change the default if they actually want a different default.

It’s good to see this on the to do list.

@jkotas This is not only for completeness and compatibility. There is a real need for the feature. It enables authorisation downstream in an asynchronous path if you don’t control all the code on the way.