In .NET WebAPI, I've created a way to have all of the authorization rules in a central location, rather than scattered throughout controllers. I'm curious why this centralization isn't done more often; are there repercussions/security concerns?
My current approach is to create a Dictionary during App_Start that contains all of my Authorization data then using a DelegatingHandler to apply the restrictions (code below). The dictionary key is a Tuple of the Controller and Action, and the value is the authorized roles. The DelegatingHandler ties into WebAPI's routing config to get which controller is called, then uses the Dictionary to determine whether the request is allowed.
Dictionary:
var authorizations = new Dictionary<Tuple<string, string>, string>();
authorizations.Add(new Tuple<string, string>("values", "get"), "public");
authorizations.Add(new Tuple<string, string>("values", "put"), "private");
DelegatingHandler:
public class SecurityDelegateHandler : DelegatingHandler
{
private readonly Dictionary<Tuple<string, string>, string> _authorizations;
public SecurityDelegateHandler(Dictionary<Tuple<string, string>, string> auth)
{
_authorizations = auth;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var config = GlobalConfiguration.Configuration;
var controllerSelector = new DefaultHttpControllerSelector(config);
var descriptor = controllerSelector.SelectController(request);
string restrictions;
if (!_authorizations.TryGetValue(
new Tuple<string, string>(descriptor.ControllerName.ToLower(),
request.Method.ToString().ToLower()), out restrictions))
{
return Task<HttpResponseMessage>.Factory.StartNew(() =>
request.CreateResponse(HttpStatusCode.Forbidden,
"Access denied on unconfigured actions"),
cancellationToken);
}
if (!(Roles.Provider).GetRolesForUser(
HttpContext.Current.User.Identity.Name).Any(r =>
restrictions.Contains(r)))
{
return Task<HttpResponseMessage>.Factory.StartNew(() =>
request.CreateResponse(HttpStatusCode.Forbidden,
"Access Denied"), cancellationToken);
}
return base.SendAsync(request, cancellationToken);
}
}
In summary, my questions are:
- Are there any problems with implementing Role Based Authorization in this way?
- Are there any good packages out there that handle centralizing authorization for WebAPI? I've looked into FluentSecurity, but that doesn't appear to support WebAPI.
Thanks!