Search code examples
wcfwifclaimsfederation

ASP.Net WCF service's Thread.CurrentPrincipal is being thrown away by some interceptor in a Federated (WIF) environment


I have a per-call WCF service that's being hosted in IIS (.svc). In the service's constructor, I set Thread.CurrentPrincipal = HttpContext.Current.User as per this article. In this case HttpContext.Current.User is of type Microsoft.IdentityModel.Claims.ClaimsPrincipal and has the claims that were sent back from my custom passive STS.

However, as soon as I step into my service operation and examine Thread.CurrentPrincipal, while this object is still of type Microsoft.IdentityModel.Claims.ClaimsIdentity, the object itself is no longer the same as HttpContext.Current.User (IsAuthenticated = false, AuthenticationType = "" and Name is null on Thread.CurrentPrincipal.Identity), whereas these values are all still filled in correctly on HttpContext.Current.User. This tells me that something is intercepting the call to the operation and incorrectly changing the current principal to some generic, empty, unauthenticated claims principal.

I checked the thread ID in the constructor as well as in the operation and it's the same in both places, and evaluating Thread.CurrentPrincipal in the immediate window after assigning from HttpContext.Current.User shows that the thread identity is being set correctly in the constructor, so something is definitely executing in between the constructor and the method, and that something is changing my Thread.CurrentPrincipal.

Does anybody have any idea what is doing this, and how I can go about preventing / fixing this behaviour?


Solution

  • When configuring a service for WIF federation, you call

    FederatedServiceCredentials.ConfigureServiceHost(this);
    

    Part of what this call does is to set up a custom ServiceAuthorizationManager of type IdentityModelServiceAuthorizationManager on the service host. This authorization manager appears to get called in between activation (construction) of the instance and execution of the operation, and it overwrites Thread.CurrentPrincipal with an instance of IClaimsPrincipal, but it doesn't seem to realize that it's running in ASP.NET Compatibility mode, so it doesn't pull the principal from HttpContext.Current.User.

    I was able to bypass this behaviour by deriving from IdentityModelServiceAuthorizationManager and overriding the CheckAccess method as follows:

    public class CustomAuthorizationManager : IdentityModelServiceAuthorizationManager
    {
        public override bool CheckAccess(System.ServiceModel.OperationContext operationContext, ref System.ServiceModel.Channels.Message message)
        {
            var result = base.CheckAccess(operationContext, ref message);
    
            var properties = operationContext.ServiceSecurityContext.AuthorizationContext.Properties;
            properties["Principal"] = System.Web.HttpContext.Current.User;
    
            return result;
        }
    }
    

    This then gets applied to the service host as follows:

    protected override void InitializeRuntime()
    {
        FederatedServiceCredentials.ConfigureServiceHost(this);
        this.Authorization.ServiceAuthorizationManager = new CustomAuthorizationManager();
        base.InitializeRuntime();
    }
    

    And now when I enter my service operation, I have the correct IClaimsPrincipal on Thread.CurrentPrincipal, so declarative PrincipalPermission now works as expected.