June 2013

Volume 28 Number 6

ASP.NET - Enabling and Customizing ASP.NET Web API Services Security

By Peter Vogel | June 2013

For the most common scenario—JavaScript in a Web page accessing a Web API service on the same site—discussing security for ASP.NET Web API is almost redundant. Provided that you authenticate your users and authorize access to the Web Forms/Views holding the JavaScript that consumes your services, you’ve probably provided all the security your services need. This is a result of ASP.NET sending the cookies and authentication information it uses to validate page requests as part of any client-side JavaScript requests to your service methods. There’s one exception (and it’s an important one): ASP.NET doesn’t automatically protect you against Cross-Site Request Forgery (CSRF/XSRF) attacks (more on that later).

In addition to CSRF, there are two scenarios when it does make sense to discuss securing your Web API services. The first scenario is when your service is consumed by a client other than a page in the same site as your ApiControllers. Those clients wouldn’t have been authenticated through Forms Authentication and wouldn’t have acquired the cookies and tokens that ASP.NET uses to control access to your services.

The second scenario occurs when you wish to add additional authorization to your services beyond what’s provided through ASP.NET security. The default authorization ASP.NET provides is based on the identity ASP.NET assigns to the request during authentication. You might wish to extend that identity to authorize access based on something other than the identity’s name or role.

Web API gives you a number of choices to address both scenarios. In fact, while I’ll discuss security in the context of accepting Web API requests, because the Web API is based on the same ASP.NET foundation as Web Forms and MVC, the tools that I’ll cover in this article are going to be familiar to anyone who has gone under the hood with security in Web Forms or MVC.

One caveat: While the Web API provides you with several choices for authentication and authorization, security begins with the host, either IIS or a host that you create when self hosting. If, for example, you want to ensure privacy in the communication between a Web API service and the client, then you should, at the very least, turn on SSL. This, however, is a responsibility of the site administrator, rather than the developer. In this article I’m going to ignore the host to concentrate on what a developer can—and should—do to secure a Web API service (and the tools that I’ll discuss here work if SSL is turned on or off).

Preventing Cross-Site Request Forgery Attacks

When a user accesses an ASP.NET site using Forms Authentication, ASP.NET generates a cookie that stipulates the user is authenticated. The browser will continue to send that cookie on every subsequent request to the site, no matter from where that request originates. This opens your site to CSRF attacks, as does any authentication scheme where the browser automatically sends authentication information previously received. If, after your site provides the browser with the security cookie, the user visits some malicious site, then that site can send requests to your service, piggy-backing on the authentication cookie the browser received earlier.

To prevent CSRF attacks, you’ll need to generate antiforgery tokens at the server and embed them in the page to be used in your client-side calls. Microsoft provides the AntiForgery class with a GetToken method that will generate tokens specific to the user who made the request (who may, of course, be the anonymous user). This code generates the two tokens and puts them in the ASP.NET MVC ViewBag where they can be used in the View:

[Authorize(Roles="manager")]
public ActionResult Index()
{
  string cookieToken;
  string formToken;
  AntiForgery.GetTokens(null, out cookieToken, out formToken);
  ViewBag.cookieToken = cookieToken;
  ViewBag.formToken = formToken;
  return View("Index");
}

Any JavaScript calls to the server will need to return the tokens as part of the request (a CSRF site won’t have these tokens and won’t be able to return them). This code, in a View, dynamically generates a JavaScript call that adds the tokens to the request’s headers:

$.ajax("https://phvis.com/api/Customers",{
  type: "get",
  contentType: "application/json",
  headers: {
    'formToken': '@ViewBag.formToken',
    'cookieToken': '@ViewBag.cookieToken' }});

A slightly more complex solution would let you use unobtrusive JavaScript by embedding the tokens in hidden fields in the View. The first step in that process would be to add the tokens to the ViewData dictionary:

ViewData["cookieToken"] = cookieToken;
ViewData["formToken"] = formToken;

Now, in the View, you can embed the data in hidden fields. The HtmlHelper’s Hidden method just needs to be passed the value of a key in ViewDate to generate the right input tag:

@Html.Hidden("formToken")

The resulting input tag will use the ViewData key for the tag’s name and id attributes and put the data retrieved from the ViewData dictionary into the tag’s value attribute. The input tag generated from the previous code would look like this:

<input id="formToken" name="formToken" type="hidden" value="...token..." />

Your JavaScript code (kept in a separate file from the View) can then retrieve the values from the input tags and use them in your ajax call:

$.ajax("https://localhost:49226/api/Customers", {
  type: "get",
  contentType: "application/json",
  headers: {
    'formToken': $("#formToken").val(),
    'cookieToken': $("#cookieToken").val()}});

You can achieve the same goals in an ASP.NET Web Forms site by using the RegisterClientScriptBlock method on the ClientScriptManager object (retrievable from the Page’s ClientScript property) to insert JavaScript code with the embedded tokens:

string CodeString = "function CallService(){" +
  "$.ajax('https://phvis.com/api/Customers',{" +
  "type: 'get', contentType: 'application/json'," +
  "headers: {'formToken': '" & formToken & "',” +
  "'cookieToken': '" & cookieToken & "'}});}"
this.ClientScript.RegisterClientScriptBlock(
  typeOf(this), "loadCustid", CodeString, true);

Finally, you’ll need to validate the tokens at the server when they’re returned by the JavaScript call. Visual Studio 2012 users who have applied the ASP.NET and Web Tools 2012.2 update will find the new Single-Page Application (SPA) template includes a ValidateHttpAntiForgeryToken filter that can be used on Web API methods. In the absence of that filter, you’ll need to retrieve the tokens and pass them to the AntiForgery class’s Validate method (the Validate method will throw an exception if the tokens aren’t valid or were generated for a different user). The code in Figure 1, used in a Web API service method, retrieves the tokens from the headers and validates them.

Figure 1 Validating CSRF Tokens in a Service Method

public HttpResponseMessage Get(){
  if (Request.Headers.TryGetValues("cookieToken", out tokens))
  {
    string cookieToken = tokens.First();
    Request.Headers.TryGetValues("formToken", out tokens);
    string formToken = tokens.First();
    AntiForgery.Validate(cookieToken, formToken);
  }
  else
  {
    HttpResponseMessage hrm =
      new HttpResponseMessage(HttpStatusCode.Unauthorized);
    hrm.ReasonPhrase = "CSRF tokens not found";
    return hrm;
  } 
  // ... Code to process request ...

Using the ValidateHttpAntiForgeryToken (rather than code inside the method) moves processing to earlier in the cycle (before, for example, model binding), which is a good thing.

Why No OAuth?

This article studiously ignores OAuth. The OAuth specifica­tion defines how tokens can be retrieved by a client from a third-party server to be sent to a service that will, in turn, validate the token with the third-party server. A discussion of how to access an OAuth token provider either from the client or the service is beyond the scope for this article.

The initial version of OAuth also isn’t a good match for the Web API. Presumably, one of the primary reasons for using the Web API is to use lighter-weight requests based on REST and JSON. That goal makes the first version of OAuth an unattractive option for Web API services. The tokens specified by the first version of OAuth are bulky and XML-based. Fortunately, OAuth 2.0 introduced a specification for a lighter-weight JSON token that’s more compact than the token from previous versions. Presumably, the techniques discussed in this article could be used to process any OAuth tokens sent to your service.

Basic Authentication

The first of the two primary responsibilities you have in securing a Web API service is authentication (the other responsibility being authorization). I’ll assume other issues—privacy, for example—are handled at the host.

Ideally, both authentication and authorization will be performed as early as possible in the Web API pipeline to avoid spending processing cycles on a request you intend to deny. This article’s authentication solutions are used very early in the pipeline—virtually as soon as the request is received. These techniques also allow you to integrate authentication with whatever user lists you’re already maintaining. The authorization techniques discussed can be applied in a variety of places in the pipeline (including as late as in the service method itself) and can work with authentication to authorize requests based on some other criteria than the user’s name or role.

You can support clients who haven’t gone through the Forms Authentication by providing your own authentication method in a custom HTTP module (I’m still assuming here that you’re not authenticating against Windows accounts but against your own list of valid users). There are two major benefits to using an HTTP module: a module participates in HTTP logging and auditing; also, modules are invoked very early in the pipeline. While these are both good things, modules do come with two costs: modules are global and are applied to all requests to the site, not just the Web API requests; also, to use authentication modules, you must host your service in IIS. Later in this article, I’ll discuss using delegating handlers that are invoked only for Web API requests and are host-agnostic.

For this example in using an HTTP module, I assume that IIS is using Basic Authentication and the credentials used to authen­ticate a user are a username and password, sent by the client (in this article, I’ll ignore Windows certification but will discuss using client certificates). I also assume that the Web API service that I’m protecting is secured using an Authorize attribute such as this, which specifies a user:

public class CustomersController : ApiController
{
  [Authorize(Users="Peter")]
  public Customer Get()
  {

The first step in creating a custom authorization HTTP module is to add a class to your service project that implements the IHttpModule and IDisposable interfaces. In the class’s Init method you’ll need to wire up two events from the HttpApplication object passed to the method. The method you attach to the AuthenticateRequest event will be called when the client’s credentials are presented. But you must also wire up the EndRequest method in order to generate the message that causes the client to send you its credentials. You’ll also need a Dispose method, but you don’t need to put anything in it to support the code used here:

public class PHVHttpAuthentication : IHttpModule, IDisposable
{
  public void Init(HttpApplication context)
  {
    context.AuthenticateRequest += AuthenticateRequests;
    context.EndRequest += TriggerCredentials;
  }
  public void Dispose()
  {
  }

An HttpClient will send credentials in response to a WWW-­Authenticate header that you include in the HTTP response. You should include that header when a request generates a 401 status code (ASP.NET will generate a 401 response code when the client is denied access to a secured service). The header must provide a hint as to the authentication method being used and the realm in which the authentication will apply (the realm can be any arbitrary string and is used to flag to the browser different areas on the server). The code to send that message is what you put in the method wired to the EndRequest event. This example generates a message that specifies that Basic authentication is being used within the PHVIS realm:

private static void TriggerCredentials(object sender, EventArgs e)
{
  HttpResponse resp = HttpContext.Current.Response;
  if (resp.StatusCode == 401)
  {
    resp.Headers.Add("WWW-Authenticate", @"Basic realm='PHVIS'");
  }
}

Within the method you’ve wired up to the AuthenticateRequest method, you’ll need to retrieve the Authorization headers the client will send as a result of receiving your 401/WWW-Authenticate message:

private static void AuthenticateRequests(object sender,
  EventArgs e)
{
  string authHeader =     
    HttpContext.Current. Request.Headers["Authorization"];
  if (authHeader != null)
  {

Once you’ve determined the client has passed Authorization header elements (and continuing with my earlier assumption that the site is using Basic Authentication), you need to parse out the data holding the username and password. The username and password are Base64-encoded and separated by a colon. This code retrieves the username and password into a two-position string array:

AuthenticationHeaderValue authHeaderVal =
  AuthenticationHeaderValue.Parse(authHeader);
if (authHeaderVal.Parameter != null)
{
  byte[] unencoded = Convert.FromBase64String(
    authHeaderVal.Parameter);
  string userpw =
    Encoding.GetEncoding("iso-8859-1").GetString(unencoded);
  string[] creds = userpw.Split(':');

As this code demonstrates, usernames and passwords are sent in clear-text. If you don’t turn on SSL then your usernames and passwords can be easily captured (and this code works even if SSL is turned on).

The next step is to validate the username and password using whatever mechanism makes sense to you. Regardless of how you validate the request (the code I use in the following example is probably too simple), your final step is to create an identity for the user that will be used in the authorization processes later in the ASP.NET pipeline.

To pass that identity information through the pipeline, you create a GenericIdentity object with the name of the identity you want to assign to the user (in the following code I’ve assumed that identity is the username sent in the header). Once you’ve created the GenericIdentity object, you must put it in the Thread class’s CurrentPrincipal property. ASP.NET also maintains a second security context in the HttpContext object and, if your host is IIS, you must support that by also setting the User property in the HttpContext’s Current property to your GenericIdentity object:

if (creds[0] == "Peter" && creds[1] == "pw")
{
  GenericIdentity gi = new GenericIdentity(creds[0]);
  Thread.CurrentPrincipal = new GenericPrincipal(gi, null);
  HttpContext.Current.User = Thread.CurrentPrincipal;
}

If you want to support role-based security, then you must pass an array of role names as the second parameter to the GenericPrincipal constructor. This example assigns every user to the manager and admin roles:

string[] roles = "manager,admin".Split(',');
Thread.CurrentPrincipal = new GenericPrincipal(gi, roles);

To integrate your HTTP module into your site’s processing, in your project’s web.config file, use the add tag within the modules element. The add tag’s type attribute must be set to a string consisting of the fully qualified class name followed by the assembly name of your module:

<modules>
  <add name="myCustomerAuth"
    type="SecureWebAPI.PHVHttpAuthentication, SecureWebAPI"/>
</modules>

The GenericIdentity object you created will work with the ASP.NET Authorize attribute. You can also access the GenericIdentity from inside a service method to perform authorization activities. You could, for example, provide different services for logged-in and anonymous users by determining if a user has been authenticated by checking the GenericIdentity object IsAuthenticated property (IsAuthenticated returns false for the Anonymous user):

if (Thread.CurrentPrincipal.Identity.IsAuthenticated)
{

You can retrieve the GenericIdentity object more simply through the User property:

if (User.Identity.IsAuthenticated)
{

Building a Compatible Client

In order to consume services protected by this module, a non-­JavaScript client must provide an acceptable username and password. To provide those credentials using the .NET HttpClient, you first create an HttpClientHandler object and set its Credentials property to a NetworkCredential object holding the username and password (or set the HttpClientHandler object’s UseDefaultCredentials property to true in order to use the current user’s Windows credentials). You then create your HttpClient object, passing the HttpClientHandler object:

HttpClientHandler hch = new HttpClientHandler();
hch.Credentials = new NetworkCredential ("Peter", "pw");
HttpClient hc = new HttpClient(hch);

With that configuration done, you can issue your request to the service. The HttpClient won’t present the credentials until it’s denied access to the service and has received the WWW-Authenticate message. If the credentials provided by the HttpClient aren’t acceptable, the service returns an HttpResponseMessage with the StatusCode of its Result set to “unauthenticated.”

The following code calls a service using the GetAsync method, checks for a successful result and (if it doesn’t get one) displays the status code returned from the service:

hc.GetAsync("https://phvis.com/api/Customers").ContinueWith(r =>
{
  HttpResponseMessage hrm = r.Result;
  if (hrm.IsSuccessStatusCode)
  {
    // ... Process response ...
  }
  else
  {
    MessageBox.Show(hrm.StatusCode.ToString());
  }
});

Assuming that you bypass the ASP.NET login process for non-­JavaScript clients, as I did here, no authentication cookies will be created and each request from the client will be validated individually. To reduce the overhead on repeatedly validating the credentials provided by the client, you should consider caching credentials you retrieve at the service (and using your Dispose method to discard those cached credentials).

Working with Client Certificates

In an HTTP module, you retrieve a client certificate object (and ensure that it’s present and valid) with code such as this:

System.Web.HttpClientCertificate cert =
  HttpContext.Current.Request.ClientCertificate;
if (cert!= null && cert.IsPresent && cert.IsValid)
{

Further along in the processing pipeline—in a service method, for example—you retrieve the certificate object (and check that one exists) with this code:

X509Certificate2 cert = Request.GetClientCertificate();
if (cert!= null)
{

If a certificate is valid and present, you can additionally check for specific values in the certificate’s properties (for example, subject or issuer).

To send certificates with an HttpClient, your first step is to create a WebRequestHandler object instead of an HttpClientHandler (the WebRequestHandler offers more configuration options than the HttpClientHandler):

WebRequestHandler wrh = new WebRequestHandler();

You can have the HttpClient automatically search the client’s certificate stores by setting the WebRequestHandler object’s ClientCertificateOptions to the Automatic value from the ClientCertificateOption enum:

wrh.ClientCertificateOptions = ClientCertificateOption.Manual;

By default, however, the client must explicitly attach certificates to the WebRequestHandler from code. You can retrieve the certificate from one of the client’s certificate stores as this example does, which retrieves a certificate from the CurrentUser’s store using the issuer’s name:

X509Store certStore;
X509Certificate x509cert;
certStore = new X509Store(StoreName.My, 
  StoreLocation.CurrentUser);  
certStore.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
x509cert = certStore.Certificates.Find(
  X509FindType.FindByIssuerName, "PHVIS", true)[0];
store.Close();

If the user has been sent a client certificate that, for some reason, isn’t going to be added to the user’s certificate store, then you can create an X509Certificate object from the certificate’s file with code like this:

x509cert = new X509Certificate2(@"C:\PHVIS.pfx");

Regardless of how the X509Certificate is created, the final steps at the client are to add the certificate to the WebRequestHandler ClientCertificates collection and then use the configured Web­RequestHandler to create the HttpClient:

wrh.ClientCertificates.Add(x509cert);
hc = new HttpClient(wrh);

Authorizing in a Self-Hosted Environment

While you can’t use an HttpModule in a self-hosted environment, the process for securing requests early in the processing pipeline of a self-hosted service is the same: Get the credentials from the request, use that information to authenticate the request and create an identity to pass to the current thread’s CurrentPrincipal property. The simplest mechanism is to create a username and password validator. To do more than just validate a username and password combination, you can create a delegating handler. I’ll first look at integrating a username and password validator.

To create a validator (still assuming that you’re using Basic Authentication), you must create a class that inherits from User­NamePasswordValidator (you’ll need to add a reference to the System.IdentityModel library to your project). The only method from the base class that you need to override is the Validate method, which will be passed the username and password sent to the service by the client. As before, once you’ve validated the username and password, you must create a GenericPrincipal object and use it to set the CurrentPrincipal property on the Thread class (because you’re not using IIS as your host, you don’t set the HttpContext User property):

public class PHVValidator :
  System.IdentityModel.Selectors.UserNamePasswordValidator
{
  public override void Validate(string userName, string password)
  {
    if (userName == "Peter" && password == "pw")
    {
      GenericIdentity gi = new GenericIdentity(username, null);
      Thread.CurrentPrincipal = gi;
    }

The following code creates a host for a controller called Customers with an endpoint of https://phvis.com/MyServices, and specifies a new validator:

partial class PHVService : ServiceBase
{
  private HttpSelfHostServer shs;
  protected override void OnStart(string[] args)
  {
    HttpSelfHostConfiguration hcfg =
      new HttpSelfHostConfiguration("https://phvis.com/MyServices");
    hcfg.Routes.MapHttpRoute("CustomerServiceRoute",
      "Customers", new { controller = "Customers" });
    hcfg.UserNamePasswordValidator = new PHVValidator;       
    shs = new HttpSelfHostServer(hcfg);
    shs.OpenAsync();

Message Handlers

To do more than validate the username and password, you can create a custom Web API message handler. Message handlers have several benefits compared to an HTTP module: message handlers aren’t tied to IIS, so security applied in a message handler will work with any host; message handlers are only used by the Web API, so they provide a simple way to perform authorization (and assign identities) for your services using a process different from that used with your Web site pages; and you can also assign message handlers to specific routes so that your security code is only invoked where it’s needed.

The first step in creating a message handler is to write a class that inherits from DelegatingHandler and override its SendAysnc method:

public class PHVAuthorizingMessageHandler: DelegatingHandler
{
  protected override System.Threading.Tasks.Task<HttpResponseMessage>
    SendAsync(HttpRequestMessage request,
      System.Threading.CancellationToken cancellationToken)
  {

Within that method (and assuming that you’re creating a per-route handler) you can set the DelegatingHandler’s InnerHandler property so that this handler can be linked into the pipeline with other handlers:

HttpConfiguration hcon = request.GetConfiguration();
InnerHandler = new HttpControllerDispatcher(hcon);

For this example, I’m going to assume that a valid request must have a simple token in its querystring (quite simple: a name/value pair of “authToken=xyx”). If the token is missing or not set to xyx, the code returns a 403 (Forbidden) status code.

I first turn the querystring into a set of name/value pairs by calling the GetQueryNameValuePairs method on the HttpRequestMessage object passed to the method. I then use LINQ to retrieve the token (or null if the token is missing). If the token is missing or invalid, I create an HttpResponseMessage with the appropriate HTTP status code, wrap it in a TaskCompletionSource object and return it:

string usingRegion = (from kvp in request.GetQueryNameValuePairs()
                      where kvp.Key == "authToken"
                      select kvp.Value).FirstOrDefault();
if (usingRegion == null || usingRegion != "xyx")
{
  HttpResponseMessage resp =
     new HttpResponseMessage(HttpStatusCode.Forbidden);
  TaskCompletionSource tsc =
     new TaskCompletionSource<HttpResponseMessage>();
  tsc.SetResult(resp);
  return tsc.Task;
}

If the token is present and set to the right value, I create a Generic­Principal object and use it to set the Thread’s CurrentPrincipal property (to support using this message handler under IIS, I also set the HttpContext User property if the HttpContext object isn’t null):

Thread.CurrentPrincipal = new GenericPrincipal(
  Thread.CurrentPrincipal.Identity.Name, null);     
if (HttpContext.Current != null)
{
  HttpContext.Current.User = Thread.CurrentPrincipal;
}

With the request authenticated through the token and the identity set, the message handler calls the base method to continue processing:

return base.SendAsync(request, cancellationToken);

If your message handler is to be used on every controller, you can add it to the Web API processing pipeline like any other message handler. However, to limit your handler to being used only on specific routes, you must add it through the MapHttpRoute method. First, instantiate your class and then pass it as the fifth parameter to MapHttpRoute (this code requires an Imports/using statement for System.Web.Http):

routes.MapHttpRoute(
  "ServiceDefault",
  "api/Customers/{id}",
  new { id = RouteParameter.Optional },
  null,
  new PHVAuthorizingMessageHandler());

Rather than set the InnerHandler within the DelegatingHandler, you can set the InnerHandler property to the default dispatcher as part of defining your route:

routes.MapHttpRoute(
  "ServiceDefault",
  "api/{controller}/{id}",
  new { id = RouteParameter.Optional },
  null,
  new PHVAuthorizingMessageHandler
  {InnerHandler = new HttpControllerDispatcher(
    GlobalConfiguration.Configuration)});

Now, instead of your InnerHandler setting being spread among multiple DelegatingHandlers, you’re managing it from the single location where you define your routes.

Extending the Principal

If authorizing requests by name and role isn’t sufficient, you can extend the authorization process by creating your own principal class by implementing the IPrincipal interface. However, to take advantage of a custom principal class, you’ll need to create your own custom authorization attribute or add custom code to your service methods.

For example, if you have a set of services that can only be accessed by users from a specific region, you could create a simple principal class that implements the IPrincipal interface and adds a Region property, as shown in Figure 2.

Figure 2 Creating a Custom Principal with Additional Properties

public class PHVPrincipal: IPrincipal
{
  public PHVPrincipal(string Name, string Region)
  {
    this.Name = Name;
    this.Region = Region;
  }
  public string Name { get; set; }
  public string Region { get; set; }
  public IIdentity Identity
  {
    get
    {
      return new GenericIdentity(this.Name);
    }
    set
    {
      this.Name = value.Name;
    }
   }
   public bool IsInRole(string role)
   {
     return true;
   }

To take advantage of this new principal class (which will work with any host), you just need to instantiate it and then use it to set the CurrentPrincipal and User properties. The following code looks for a value in the request’s query string associated with the name “region.” After retrieving that value, the code uses it to set the principal’s Region property by passing the value to the class’s constructor:

string region = (from kvp in request.GetQueryNameValuePairs()
                 where kvp.Key == "region"
                 select kvp.Value).FirstOrDefault();
Thread.CurrentPrincipal = new PHVPrincipal(userName, region);

If you’re working in the Microsoft .NET Framework 4.5, rather than implementing the IPrincipal interface, you should inherit from the new ClaimsPrincipal class. ClaimsPrincipal supports both claims-based processing and integration with Windows Identity Foundation (WIF). That, however, is out of scope for this article (I’ll address that topic in an upcoming article on claims-based security).

Authorizing a Custom Principal

With a new principal object in place you can create an authorization attribute that takes advantage of the new data carried by the principal. First, create a class that inherits from System.Web.Http.AuthorizeAttri­bute and overrides its IsAuthorized method (this is a different pro­cess from the ASP.NET MVC practice where you create new Authorization attributes by extending System.Web.Http.Filters.Autho­rizationFilterAttribute). The IsAuthorized method is passed an HttpActionContext, whose properties can be used as part of your authorization process. However, this example just needs to extract the principal object from the Thread’s CurrentPrincipal property, cast it to the custom principal type and check the Region property. If authorization succeeds, the code returns true. If authorization fails, you need the ActionContext Response property to create a custom response before returning false, as shown in Figure 3.

Figure 3 Filtering a Custom Principal Object

public class RegionAuthorizeAttribute : System.Web.Http.AuthorizeAttribute
{
  public string Region { get; set; }
  protected override bool IsAuthorized(HttpActionContext actionContext)
  {
    PHVPrincipal phvPcp = Thread.CurrentPrincipal as PHVPrincipal;
    if (phvPcp != null && phvPcp.Region == this.Region)
    {
      return true;
    }
    else
    {
      actionContext.Response =
        new HttpResponseMessage(
          System.Net.HttpStatusCode.Unauthorized)
        {
          ReasonPhrase = "Invalid region"
        };
      return false;
    }        
  }
}

Your custom authorization filter can be used just like the default ASP.NET Authorize filter. Because this filter has a Region property, that property must be set to the acceptable region for this method as part of decorating a service method with it:

[RegionAuthorize(Region = "East")]
public HttpResponseMessage Get()
{

For this example, I’ve chosen to inherit from the Authorize­Attribute because my authorization code is purely CPU bound. If my code needed to access some network resource (or do any I/O at all), a better choice would’ve been to implement the IAuthorization­Filter interface because it supports making asynchronous calls.

As I said at the start of this article: The typical Web API scenario doesn’t require additional authorization, except to protect against CSFR exploits. But when you do need to extend the default security system, the Web API provides numerous choices throughout the processing pipeline where you can integrate whatever protection you need. And it’s always better to have choices.


Peter Vogel is a principal at PH&V Information Services, specializing in ASP.NET development with expertise in service-oriented architecture, XML, database and UI design.

Thanks to the following technical experts for reviewing this article: Dominick Baier (thinktecture GmbH & Co KG), Barry Dorrans (Microsoft) and Mike Wasson (Microsoft)
Mike Wasson (mwasson@microsoft.com) is a programmer-writer at Microsoft. He currently writes about ASP.NET, focused on Web API.

Barry Dorrans (Barry.Dorrans@microsoft.com) is a security developer at Microsoft, working with the Azure Platform team. He wrote “Beginning ASP.NET Security” and was a Developer Security MVP before joining Microsoft. Despite this he still misspells encryption on a regular basis.

Dominick (dominick.baier@thinktecture.com) is a security consultant at thinktecture (thinktecture.com). His main focus is identity and access control in distributed applications and he is the creator of the popular open source projects IdentityModel and IdentityServer. You can find his blog at leastprivilege.com.