Claims Auth with OWIN Self Hosted WebApi
asp.net-web-api, authentication, c#, claims-based-identity, owin
Solution
In the message handler, set the principal like this.
request.GetRequestContext().Principal = principal;
Do not use
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
HttpContext.Current.User = principal;
UPDATE
It has been a while since I worked on .NET 4.0/2012/Web API <2. So, I cannot answer for sure. But with OWIN hosting, principal must be set in the OWIN context. `OwinHttpRequestContext` sets both `Thread.CurrentPrincipal` and the principal in OWIN context. By using `request.GetRequestContext().Principal`, these details are hidden from you. To make long story short, I believe if you some how set the principal in OWIN context, this will work. Not sure how you can do that from web API message handler. You can do that from OWIN middleware.
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute("default", "api/{controller}/{id}");
//config.MessageHandlers.Add(new PresharedKeyAuthorizer());
app.Use((IOwinContext context, Func<Task> next) =>
{
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Name, "superstar"));
var identity = new ClaimsIdentity(claims, "PresharedKey");
var principal = new ClaimsPrincipal(identity);
context.Request.User = principal;
return next.Invoke();
});
app.UseWebApi(config);
}
Problem
I am self hosting WebApi with the following configuration: Visual Studio 2012 / .NET 4.0 ``` public void Configuration(IAppBuilder appBuilder) { var config = new HttpConfiguration(); // authentication config.MessageHandlers.Add(new Shield.PresharedKeyAuthorizer()); // routing config.Routes.MapHttpRoute( name: "Default", routeTemplate: "{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); appBuilder.UseWebApi(config); } ``` I have a simple test setup with the following `DelegatingHandler` to create a claim and attach it to the current thread. ``` public class PresharedKeyAuthorizer : DelegatingHandler { protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { var claims = new List<Claim>(); claims.Add(new Claim(ClaimTypes.Name, "superstar")); var identity = new ClaimsIdentity(claims, "PresharedKey"); var principal = new ClaimsPrincipal(identity); Thread.CurrentPrincipal = principal; if (HttpContext.Current != null) HttpContext.Current.User = principal; return base.SendAsync(request, cancellationToken); } } ``` However, when I hit the `ApiController` that is marked with the `Authorize` attribute, it doesn't recognize the authentication. ``` [Authorize] public class FilesController : ApiController { public IEnumerable<string> Get() { return new string[] { "Secure File A", "Secure File B" }; } } ``` Removing the `Authorize` attribute and setting a breakpoint, I can see that RequestContext.Principal property is indeed null. The request works fin without the `Authorize` attribute, so I know the setup of the self hosting is correct, but I must be missing something in the authentication pipeline. What am I missing to allow that claim to work against the `Authorize` attribute? This related answer with the same approach appears to work when hosted by IIS: https://stackoverflow.com/a/14872968/118224