I set the current user to a ClaimsIdentity in a WebAPI Messagehandler derived from DelegateHandler, and retrieve the username and user id from the controller.
It used to work quite fine. Now something has changed (In RC, and probably the last Preview version too) and instead of getting my ClaimsIdentity in the ApiController I suddenly get a WindowsIdentity (unauthenticated, though). I thought maybe some window authentication was kicking in, but I've never used that, and it is disabled under the properties window for the project (also the WIndowsIdentify I receive is not actually authenticated).
It's practically impossible to find examples with the new RC. Did something break, or am I doing something wrong? While I'm at it, some info on the new AuthenticationFilters would be awesome, thanks!
I can set breakpoints and trace the Identity being set correctly to Thread.Principal AND to HttpContext.Current.User in the messagehandler, and suddenly it's gone in the controller. I can send a demo project if required, but since its more than 4mb I can't attach it.
Key elements:
public class AuthHandler : DelegatingHandler
{
protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
string apiKey = request.GetQueryNameValuePairs().FirstOrDefault(k => k.Key == "apiKey").Value;
if (!string.IsNullOrWhiteSpace(apiKey))
{
string[] token = apiKey.Split('*');
var claims = new List<Claim>{
new Claim(ClaimTypes.Name, token[1]),
new Claim(ClaimTypes.NameIdentifier, token[0])
};
var principal = new ClaimsPrincipal(new[] { new ClaimsIdentity(claims, "Digest") });
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
HttpContext.Current.User = principal;
}
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
response = request.CreateResponse(HttpStatusCode.Unauthorized);
}
return response;
}
}
}
Test Controller:
public IEnumerable<string> Get()
{
var id = (System.Security.Claims.ClaimsIdentity)User.Identity;
if (id.Claims.FirstOrDefault(i => i.Type == System.Security.Claims.ClaimTypes.NameIdentifier) != null)
{
var userId = id.Claims.FirstOrDefault(i => i.Type == System.Security.Claims.ClaimTypes.NameIdentifier).Value;
return new string[] { id.Name, userId };
}
return new string[] { "No", "User", "Returned" };
}
Test call:
http://localhost:1646/api/test?apiKey=12345667*myusername
Comments: The old way of setting the principal on Thread.CurrentPrincipal and HttpContext.Current.User works perfectly with the nightly build (13th Sept 2013), so the fix has done the trick! For anyone else experiencing the same problem, the new method of setting the principal on the HttpRequestContext through request.GetRequestContext().Principal from a messagehandler also works in the RC and the nightly build, (and presumably going forward) and so it should be the preferred method I guess. Thanks again for the help with the authentication filter - a much nicer way of handling it.
It used to work quite fine. Now something has changed (In RC, and probably the last Preview version too) and instead of getting my ClaimsIdentity in the ApiController I suddenly get a WindowsIdentity (unauthenticated, though). I thought maybe some window authentication was kicking in, but I've never used that, and it is disabled under the properties window for the project (also the WIndowsIdentify I receive is not actually authenticated).
It's practically impossible to find examples with the new RC. Did something break, or am I doing something wrong? While I'm at it, some info on the new AuthenticationFilters would be awesome, thanks!
I can set breakpoints and trace the Identity being set correctly to Thread.Principal AND to HttpContext.Current.User in the messagehandler, and suddenly it's gone in the controller. I can send a demo project if required, but since its more than 4mb I can't attach it.
Key elements:
public class AuthHandler : DelegatingHandler
{
protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
string apiKey = request.GetQueryNameValuePairs().FirstOrDefault(k => k.Key == "apiKey").Value;
if (!string.IsNullOrWhiteSpace(apiKey))
{
string[] token = apiKey.Split('*');
var claims = new List<Claim>{
new Claim(ClaimTypes.Name, token[1]),
new Claim(ClaimTypes.NameIdentifier, token[0])
};
var principal = new ClaimsPrincipal(new[] { new ClaimsIdentity(claims, "Digest") });
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
HttpContext.Current.User = principal;
}
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
response = request.CreateResponse(HttpStatusCode.Unauthorized);
}
return response;
}
}
}
Test Controller:
public IEnumerable<string> Get()
{
var id = (System.Security.Claims.ClaimsIdentity)User.Identity;
if (id.Claims.FirstOrDefault(i => i.Type == System.Security.Claims.ClaimTypes.NameIdentifier) != null)
{
var userId = id.Claims.FirstOrDefault(i => i.Type == System.Security.Claims.ClaimTypes.NameIdentifier).Value;
return new string[] { id.Name, userId };
}
return new string[] { "No", "User", "Returned" };
}
Test call:
http://localhost:1646/api/test?apiKey=12345667*myusername
Comments: The old way of setting the principal on Thread.CurrentPrincipal and HttpContext.Current.User works perfectly with the nightly build (13th Sept 2013), so the fix has done the trick! For anyone else experiencing the same problem, the new method of setting the principal on the HttpRequestContext through request.GetRequestContext().Principal from a messagehandler also works in the RC and the nightly build, (and presumably going forward) and so it should be the preferred method I guess. Thanks again for the help with the authentication filter - a much nicer way of handling it.