Inject IPrincipal in ASP.NET Web API Controller

In ASP.NET MVC the current IPrincipal can be automatically made available to action methods by implementing the System.Web.Mvc.IModelBinder interface and returning the current authenticated user from HttpContext.User. We can accomplish the same in Web API using the HttpParameterBinding class.

namespace App.Web.Http.Helpers
{
    public class PrincipalParameterBinding : HttpParameterBinding
    {
        public PrincipalParameterBinding(HttpParameterDescriptor des)
            : base(des) { }

        public override Task ExecuteBindingAsync(
            ModelMetadataProvider metadataProvider,
            HttpActionContext actionContext, 
            CancellationToken cancellationToken)
        {
            SetValue(actionContext, Thread.CurrentPrincipal);

            return Task.FromResult<object>(null);
        }
    }
}

Add the rule to the HttpConfiguration during startup.

using App.Web.Http.Helpers;

namespace App.Web  
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // add the rule to the collection
            config.ParameterBindingRules
                  .Add(typeof(IPrincipal), 
                       des => new PrincipalParameterBinding(des));
        }
    }
}

Now a method like this will have its IPrincipal parameter automatically populated.

[RoutePrefix("api")]
public class MainController : ApiController  
{
    [Route("whoami")]
    public string GetPrincipal(IPrincipal user)
    {
        return user.Identity.Name;
    }
}

If we prefer not to add it globally, we will need a [ParameterBinding] attribute to decorate the method parameter.

namespace App.Web.Http.Helpers  
{
    public class IPrincipalParameterAttribute : ParameterBindingAttribute
    {
        public override HttpParameterBinding GetBinding(
            HttpParameterDescriptor desc)
        {
            if (desc.ParameterType == typeof(IPrincipal))
            {
                return new PrincipalParameterBinding(desc);
            }

            return parameter.BindAsError("Expected type IPrincipal");
        }
    }
}

Apply the [IPrincipalParameter] attribute to the method parameter.

public string GetPrincipal(
    [IPrincipalParameter]
    IPrincipal user) { ... }