September 2014

Volume 29 Number 9

ASP.NET : ASP.NET Web API Security Filters

Badrinarayanan Lakshmiraghavan | September 2014

Authentication and authorization are cornerstones of application security. Authentication establishes the identity of a user by validating the credentials provided, while authorization determines whether a user is allowed to perform a requested action. A secured Web API authenticates requests and authorizes access to the resource requested based on the identity established.

You can implement authentication in ASP.NET Web API using the extensibility points available in the ASP.NET Web API pipeline, as well as using options provided by the host. With the first version of ASP.NET Web API, a common practice is to use an authorization filter or an action filter to implement authentication. ASP.NET Web API 2 introduces a new authentication filter dedicated to the process. This new extensibility point allows authentication and authorization concerns to be cleanly separated. In this article, I’m going to introduce you to these two security filters and show you how to use them to implement authentication and authorization as separate concerns in ASP.NET Web API.

Options for Implementing Security Aspects

Authentication and authorization in ASP.NET Web API can be implemented using the extensibility points offered by the host, as well as those available in the ASP.NET Web API pipeline itself. Host-based options include HTTP modules and OWIN middleware components, while ASP.NET Web API extensibility options consist of message handlers, action filters, authorization filters, and authentication filters.

Host-based options integrate well into the host pipeline and are capable of rejecting invalid requests earlier in the pipeline. The ASP.NET Web API extensibility options, on the other hand, offer a finer level of control over the authentication process. That is, you’ll be able to set different authentication mechanisms for different controllers and even different action methods. The trade-off is better integration with the host and early rejection of bad requests versus the authentication granularity. Apart from these general characteristics, each option has its own pros and cons, as I’ll cover in the sections that follow.

HTTP Module This is an option for Web APIs running on IIS. HTTP modules allow security code to execute early as part of the IIS pipeline. The principal established from an HTTP module is available to all components, including the IIS components running later in the pipeline. For example, when the principal is established by an HTTP module in response to the AuthenticateRequest event, the username of the principal gets logged correctly in the cs-username field in IIS logs. The biggest drawback with HTTP modules is the lack of granularity. HTTP modules run for all requests coming into the application. For a Web application with different capabilities such as HTML markup generation, Web APIs and so on, having an HTTP module enforcing authentication in one way is generally not a flexible-enough approach. Another disadvantage with using an HTTP module is the dependency on the host—IIS, in this case.

OWIN Middleware This is another host-related option, available with OWIN hosts. ASP.NET Web API 2 fully supports OWIN. Perhaps the most compelling reason for using OWIN middleware for security is that the same middleware can work across different frameworks. This means you can use multiple frameworks such as ASP.NET Web API, SignalR and so on in your application, yet use common security middleware. However, OWIN middleware’s minimal granularity could be a shortcoming, because OWIN middleware runs in the OWIN pipeline and gets invoked typically for all requests. Also, OWIN middleware can be used only with OWIN-compatible hosts, although this dependency is comparatively better than taking dependency on a specific host/server such as IIS, as is the case with HTTP modules. A point worth noting here is that OWIN middleware can run in the (IIS-integrated) ASP.NET pipeline, thanks to the package Microsoft.Owin.Host.SystemWeb.

Message Handler An extensibility option provided by ASP.NET Web API, the greatest benefit in using a message handler for security is it’s a concept of the ASP.NET Web API framework and, hence, doesn’t depend on the underlying host or server. Also, a message handler runs only for Web API requests. The downside of using a message handler is the lack of finer control. A message handler can be configured to run as a global handler for all requests or for a specific route. For a given route, you can have multiple controllers. All these controllers and the action methods they contain must share the same authentication enforced by the message handler configured for that route. In other words, the lowest granularity for authentication implemented by a message handler is at the route level.

Action Filter Another extensibility option provided by ASP.NET Web API is the action filter. However, from the perspective of implementing authentication, it isn’t a viable option simply because it runs after the authorization filters are run in the ASP.NET Web API pipeline. For authentication and authorization to work correctly, authentication must precede authorization.

Authorization Filter Yet another extensibility option provided by ASP.NET Web API is the authorization filter. One of the most common ways of implementing custom authentication for scenarios requiring more granularity than message handlers can offer is to use an authorization filter. The major problem in using an authorization filter for both authentication and authorization is that the order of execution of authorization filters isn’t guaranteed by ASP.NET Web API. Basically, this means your authorization filter performing authorization could very well run before your authorization filter performing authentication could run, making the authorization filter option equally unsuitable for authentication as the action filter option is.

Authentication Filter The focal point of this article, this is the newest extensibility option available with ASP.NET Web API 2. Authentication filters run after message handlers but before all other filter types. Therefore, they’re a better choice for implementing authentication concerns. Most important, authentication filters run before authorization filters. By using a filter that specifically targets either authentication or authorization, you can separate authentication and authorization concerns.

Moreover, authentication filters offer a level of control or granularity that makes them particularly useful. Take the case of a Web API designed to be consumed by both native mobile applications and browser-based AJAX applications. The mobile app might present a token in the HTTP Authorization header while the AJAX app might use an authentication cookie as a credential. Further, suppose a subset of the API is sensitive and available only for the native mobile app and you want to ensure the action methods are accessed only by presenting a token and not a cookie (cookies are susceptible to cross-site request forgery [XSRF], while a token in an HTTP Authorization header is not). In this case, authentication must happen at a finer level of granularity than is possible with a host-based option or even a message handler. An authentication filter fits this use case perfectly. You can apply the authentication filter based on the token on all those controllers or action methods where they must be used and the authentication filter based on the cookie in other places. Suppose, in this scenario, you have a few common action methods and you want them accessible through either a token or a cookie. You can simply apply both the cookie and token authentication filters on those common action methods and one of the filters will be able to successfully authenticate. This kind of control is the biggest value authentication filters bring to the table. When granular control of authentication is required, the right approach is to implement authentication concerns through an authentication filter and authorization concerns through an authorization filter.

It’s worth mentioning here that the out-of-box authentication filter, HostAuthenticationFilter, enables ASP.NET Web API authentication via OWIN middleware. While OWIN authentication middleware runs in a pipeline and tries to “actively” authenticate the incoming requests, it can also be configured to authenticate the request “passively,” only when asked. The HostAuthenticationFilter allows the running of passive OWIN authentication middleware by name later in the Web API pipeline. This approach enables authentication code that can be shared across frameworks (including the OWIN authentication middleware Microsoft provides) while still allowing per-action granularity for authentication.

Although you can mix host-level authentication with more-granular Web API pipeline-based authentication, you have to carefully consider how host-level authentication can affect Web API authentication. For example, you could have cookie-based authentication middleware at the host-level meant to be used with other frameworks, say ASP.NET MVC, but letting Web API use the cookie-based principal makes it susceptible to attacks such as XSRF. To help in such situations, the SuppressDefaultHostAuthentication extension method enables Web API to ignore any authentication configured at the host level. The default Web API Visual Studio template has cookies enabled at the host level, and uses bearer tokens at the Web API level. Because cookies are enabled at the host level and require XSRF mitigation, the template also uses SuppressDefaultHostAuthentication to prevent the Web API pipeline from using the cookie-based principal. This way, Web API will use only the token-based principal and you don’t need to build mechanisms for Web API to defend against XSRF attacks.

Making Authentication and Authorization Filters Work in Tandem

In the ASP.NET Web API pipeline, authentication filters run first—followed by the authorization filters— for the simple reason that authorization depends on the identity established, which is the outcome of authentication. Here’s how you can design the authentication and authorization filters to work together to secure ASP.NET Web API.

The basic tenet of this design is giving the authentication filter the one and only responsibility of validating the credential, and not having it deal with any other concerns. For example, the authentication filter will not reject a request with the 401 Unauthorized status code if credentials aren’t present. It simply doesn’t establish an authenticated identity, and leaves the question of how to handle anonymous requests to the authorization stage.  An authentication filter basically takes three types of actions:

  1. If the credential of interest isn’t present in the request, the filter does nothing.
  2. If credentials are present and found to be valid, the filter establishes an identity in the form of an authenticated principal.
  3. If credentials are present but found to be invalid, the filter notifies the ASP.NET Web API framework by setting an error result, which basically results in an “unauthorized” response that gets sent back to the requestor.

If none of the authentication filters running in the pipeline can detect an invalid credential, the pipeline continues to run, even if an authenticated identity hasn’t been established. It’s up to the components running later in the pipeline to decide how to handle this anonymous request.

At the most fundamental level, an authorization filter simply checks whether the established identity is an authenticated identity. However, an authorization filter can also ensure that:

  • The username of the authenticated identity is on the list of allowed users.
  • At least one of the roles associated with the authenticated identity is on the list of allowed roles.

While the out-of-box authorization filter performs only the role-based access control as just described, a custom authorization filter that derives from the out-of-box authorization filter can perform claims-based access control by checking the claims that are part of the identity established by the authentication filter.

If all the authorization filters are happy, the pipeline continues to execute and eventually the action method of the API controller generates a response for the request. If the identity isn’t established or if there’s a mismatch in terms of the username or the role requirements, the authorization filter rejects the request with a 401 Unauthorized response. Figure 1 illustrates the part played by the two filters in three scenarios: credentials not present, invalid credentials present and valid credentials present.

Security Filters in ASP.NET Web API Pipeline
Figure 1 Security Filters in ASP.NET Web API Pipeline

Creating an Authentication Filter

An authentication filter is a class implementing the IAuthenticationFilter interface. This interface has two methods: AuthenticateAsync and  ChallengeAsync, as shown by the following:

public interface IAuthenticationFilter : IFilter
{
  Task AuthenticateAsync(HttpAuthenticationContext context, 
    CancellationToken cancellationToken);
  Task ChallengeAsync(HttpAuthenticationChallengeContext context,
    CancellationToken cancellationToken);
}

The AuthenticateAsync method accepts HttpAuthentication­Context as an argument. This context is how the AuthenticateAsync method communicates the result of the authentication process back to the ASP.NET Web API framework. If the request message contains authentic credentials, the Principal property of the passed-in HttpAuthenticationContext object is set to the authenticated principal. If the credentials are invalid, the ErrorResult property of the HttpAuthenticationContext parameter is set to UnauthorizedResult. If the request message doesn’t contain the credential at all, the AuthenticateAsync method takes no action. The code in Figure 2 shows a typical implementation of the AuthenticateAsync method covering these three scenarios. The authenticated principal used in this example is a ClaimsPrincipal with just name and role claims.

Figure 2 The AuthenticateAsync Method

public Task AuthenticateAsync(HttpAuthenticationContext context,
  CancellationToken cancellationToken)
{
  var req = context.Request;
  // Get credential from the Authorization header 
  //(if present) and authenticate
  if (req.Headers.Authorization != null &&
    "somescheme".Equals(req.Headers.Authorization.Scheme,
      StringComparison.OrdinalIgnoreCase))
  {
    var creds = req.Headers.Authorization.Parameter;
    if(creds == "opensesame") // Replace with a real check
    {
      var claims = new List<Claim>()
      {
        new Claim(ClaimTypes.Name, "badri"),
        new Claim(ClaimTypes.Role, "admin")
      };
      var id = new ClaimsIdentity(claims, "Token");
      var principal = new ClaimsPrincipal(new[] { id });
      // The request message contains valid credential
      context.Principal = principal;
    }
    else
    {
      // The request message contains invalid credential
      context.ErrorResult = new UnauthorizedResult(
        new AuthenticationHeaderValue[0], context.Request);
    }
  }
  return Task.FromResult(0);
}

You use the AuthenticateAsync method to implement the core authentication logic of validating the credentials in the request, and the ChallengeAsync method to add the authentication challenge. An authentication challenge is added to a response when the status code is 401 Unauthorized, and to inspect the status code, you need the response object. But the ChallengeAsync method doesn’t allow you to inspect the response or set the challenge directly. In fact, this method executes before the action method, in the request processing part of the Web API pipeline. However, the parameter of the ChallengeAsync method HttpAuthenticationChallengeContext allows an action result object (IHttpActionResult) to be assigned to the Result property. The ExecuteAsync method of the action result object awaits the task producing the response, inspects the response status code, and adds the WWW-Authenticate response header.  The code in Figure 3 shows a typical implementation of the ChallengeAsync method. In this example, I just add a hardcoded challenge. The ResultWithChallenge class is the action result class I created to add the challenge.

Figure 3 The ChallengeAsync Method

public Task ChallengeAsync(HttpAuthenticationChallengeContext context,
  CancellationToken cancellationToken)
{
  context.Result = new ResultWithChallenge(context.Result);
  return Task.FromResult(0);
}
public class ResultWithChallenge : IHttpActionResult
{
  private readonly IHttpActionResult next;
  public ResultWithChallenge(IHttpActionResult next)
  {
    this.next = next;
  }
  public async Task<HttpResponseMessage> ExecuteAsync(
    CancellationToken cancellationToken)
  {
    var response = await next.ExecuteAsync(cancellationToken);
    if (response.StatusCode == HttpStatusCode.Unauthorized)
    {
      response.Headers.WwwAuthenticate.Add(
        new AuthenticationHeaderValue("somescheme", "somechallenge"));
    }
    return response;
  }
}

The following code shows the completed filter class:

public class TokenAuthenticationAttribute : 
  Attribute, IAuthenticationFilter
{
  public bool AllowMultiple { get { return false; } }
  // The AuthenticateAsync and ChallengeAsync methods go here
}

In addition to implementing the IAuthenticationFilter interface, I derive from Attribute so this class can be applied as an attribute at the class (controller) level or method (action method) level.

Thus, you can create an authentication filter with the single responsibility of authenticating a specific credential (a fictitious token in this example). An authentication filter has no authorization logic; its only purpose is to handle authentication: establishing an identity, if any, (while processing the request message) and returning a challenge, if any (while processing the response message). Authorization filters handle authorization concerns, such as checking whether the identity is an authenticated identity or whether it’s on the allowed list of users or roles.

Using an Authorization Filter

The fundamental goal of using an authorization filter is to perform authorization—to determine whether a user should be allowed access to a requested resource. Web API provides an implementation of an authorization filter called AuthorizeAttribute. Applying this filter ensures the identity is an authenticated identity. You can also configure the authorize attribute with a list of specific usernames and the roles to allow. The code in Figure 4 shows the authorization filter applied at different levels (globally, at the controller level and at the action method level), using different attributes of the identity to authorize. The filter in this example globally ensures the identity is authenticated. The filter used at the controller level ensures the identity is authenticated and that at least one role associated with the identity is “admin.” The filter used at the action method level ensures the identity is authenticated and that the username is “badri.” One point to note here is that the authorization filter at the action method level also inherits the filters from the controller and global levels. Hence, for authorization to succeed all filters must pass: the username must be “badri,” one of the roles must be “admin” and the user must be authenticated.

Figure 4 Using an Authorization Filter at Three Different Levels

public static class WebApiConfig
{
  public static void Register(HttpConfiguration config)
  {
    // Other Web API configuration code goes here
    config.Filters.Add(new AuthorizeAttribute()); // Global level
  }
}
[Authorize(Roles="admin")] // Controller level
public class EmployeesController : ApiController
{
  [Authorize(Users="badri")] // Action method level
  public string Get(int id)
  {
    return “Hello World”;
  }
}

The out-of-box AuthorizeAttribute is extremely useful, but if more customization is desired, you can sub-class it to implement additional authorization behavior. The following code shows a custom authorization filter:

public class RequireAdminClaimAttribute : AuthorizeAttribute
{
  protected override bool IsAuthorized(HttpActionContext context)
  {
    var principal =
      context.Request.GetRequestContext().Principal as ClaimsPrincipal;
    return principal.Claims.Any(c => c.Type ==
      "https://yourschema/identity/claims/admin"
      && c.Value == "true");
  }
}

This filter simply checks for an “admin” custom claim, but you can use a principal and any other additional information from the HttpActionContext to perform custom authorization here.

In the first version of ASP.NET Web API, custom authorization filters were often misused to implement authentication, but with ASP.NET Web API 2, authentication filters now have their own place in the pipeline and this helps the development of clean, modular code with authentication and authorization concerns clearly separated.

Filter Overrides

As I explained earlier, an authorization filter can be applied at the action method level, the controller level or globally. By specifying the Authorize filter globally, you can enforce authorization on all action method invocations across all controllers. If you want a few methods to be exempted from the globally configured check, it’s easier to accomplish this using the AllowAnonymous attribute.

The code in Figure 5 shows the use of the AllowAnonymous attribute at the controller level. Though the authorization filter is applied globally, the AllowAnonymous attribute used with PublicResourcesController exempts the requests coming to this controller from getting authorized.

Figure 5 Using the AllowAnonymous Attribute

public static class WebApiConfig
{
  public static void Register(HttpConfiguration config)
  {
    // Other Web API configuration code goes here
    config.Filters.Add(new AuthorizeAttribute()); // Global level
  }
}
[AllowAnonymous]
public class PublicResourcesController : ApiController
{
  public string Get(int id)
  {
    return “Hello World”;
  }
}

AllowAnonymous provides a way a specific action can override authorization configured by a higher-level authorization filter. However, AllowAnonymous only allows you to override authorization. Suppose you want most actions to authenticate using HTTP Basic authentication, but one action to authenticate using just tokens. It would be nice to configure token authentication globally but then override authentication for this one action, similarly to how AllowAnonymous overrides authorization.

ASP.NET Web API 2 introduces a new filter type to address this scenario, the override filter. Unlike AllowAnonymous, the override filters introduced in ASP.NET Web API 2 can work with any type of filter. Override filters, as the name suggests, override the filters configured at higher levels. To override the authentication filters configured at higher levels, use the out-of-box attribute OverrideAuthentication. If you have an authentication filter applied globally and want to stop it from running for a specific action method or controller, you can simply apply OverrideAuthentication at the desired level.

Override filters are useful for far more than simply stopping certain filters from running. Say you have two authentication filters, one for authenticating a security token and another for authenticating a username/password in an HTTP basic scheme. Both filters are applied globally, making your API flexible enough to accept either the token or username/password. The following code shows the two authentication filters applied globally:

public static class WebApiConfig
{
  public static void Register(HttpConfiguration config)
  {
    // Other Web API configuration code goes here
    config.Filters.Add(new TokenAuthenticator());
    config.Filters.Add(new HttpBasicAuthenticator(realm: "Magical"));
  }
}

Now, perhaps you’d like to ensure only the token is used as a credential to access a specific action method. How will OverrideAuthentication enable you to meet this need, given it just stops all filters from running? Here’s the important characteristic of the override filters—they wipe out all filters specified at higher levels but don’t remove filters specified at the same level as themselves. This basically means you can add one or more authentication filter at a specific level while eliminating everything else at higher levels. Going back to the requirement of using just the token as a credential for accessing a specific action method, you can simply specify the OverrideAuthentication attribute and TokenAuthenticator at the action method level, as shown in the following code (this ensures only TokenAuthenticator is run for the action method GetAllowedForTokenOnly):

public class EmployeesController : ApiController
{
  [OverrideAuthentication] // Removes all authentication filters
  [TokenAuthenticator] // Puts back only the token authenticator
  public string GetAllowedForTokenOnly(int id)
  {
    return “Hello World”;
  }
}

Thus, the override filters introduced with ASP.NET Web API 2 provide a lot more flexibility in terms of specifying filters globally and running filters at lower levels selectively only where the global behavior has to be overridden.

In addition to the OverrideAuthentication attribute, there’s also an out-of-box attribute called OverrideAuthorization that removes authorization filters specified at higher levels. Compared with AllowAnonymous, the difference is that OverrideAuthorization removes only the higher-level authorization filters. It doesn’t remove authorization filters specified at the same level as itself. AllowAnony­mous makes ASP.NET Web API skip the authorization process all together—even if there are authorization filters specified at the same level as AllowAnonymous, they’re simply ignored.

Wrapping Up

You can implement authentication in ASP.NET Web API using the options provided by the host, as well as the extensibility points provided by the ASP.NET Web API pipeline. Host-based options integrate well into the host pipeline and reject invalid requests earlier in the pipeline. ASP.NET Web API extensibility points offer a finer level of control over the authentication process. When you require more control of authentication, for example, you want to use different authentication mechanisms for different controllers or even different action methods, the right approach is to implement authentication concerns through an authentication filter and authorization concerns through an authorization filter.


Badrinarayanan Lakshmiraghavan is a senior consulting architect with a Fortune 500 company. He’s the author of the books, “Pro ASP.NET Web API Security” and “Practical ASP.NET Web API” both published by Apress in 2013. He occasionally blogs at lbadri.wordpress.com.

Thanks to the following Microsoft technical expert for reviewing this article: David Matson
David Matson works at Microsoft as a software developer. He is a member of the MVC 5 and Web API 2 product team.