Making your ASP.NET Web API’s secure

Note: Code for this example is on Google Docs.

SecureWebAPI
SecureWebAPITest

NOTE: DO NOT USE THE PUBLIC/PRIVATE KEYS IN THIS EXAMPLE IN YOUR PRODUCTION APPLICATION!!!! BE SURE TO GENERATE YOUR OWN KEYS!! OTHERWISE, YOUR APPLICATION WILL NOT BE SECURE AS THE PRIVATE KEY WILL BE IN THE FIELD!!!! :-)

Recently, I’ve been exploring the new ASP.NET Web API. So far, I’ve been impressed with how easy it is to build RESTful web interfaces. In the examples I’ve published, none have been secure. In the real world – the world that exists beyond the world of samples and demos – security is a matter than cannot be brushed aside. In this post, I squarely tackle that issue by showing you an approach that locks down and secures your ASP.NET Web API.

In research this topic, looking what others have done this far, I came away with a lot of approaches that in my opinion, where too complicated and quite frankly, a pain to setup. It was also questionable what the value proposition was for all that pain. In other words, how was my site made more secure by implementing one approach or another. Two big topics that I won’t discuss today are oAuth and OpenID. Sure, those approaches each solve a segment of the overall problem. oAuth is about authorization. For example, I can use my credentials in Twitter to gain access to another site. The relationship with oAuth is one app authorizing another app. OpenID on the other hand is about authentication. I have an Openid and I use that as the basis for an application to authenticate who I am – without the need for that application to maintain my security details. With OpenID, it’s one security mechanism used for many applications. In any application, you need to be both authenticated and authorized. Once a user gets access to an application, the process still needs to be secure – especially if HIPPA related type data is presented.

For many applications, OpenID or oAuth can work fine. But for a Web API, it really does not work. First, RESTful apis are just that – RESTful – which means they are stateless. To be effective, we cannot make assumptions from one call to the next. We have to supply credentials with each and every request, whether it is a GET, POST, PUT or DELETE. Like Any API, a Web API should, in my opinion be self sufficient as to controlling how people get to the site. Indeed, things like oAuth and OpenID can work. However, I think the process can be made simpler – with techniques that have been around a while and are as relevant as ever – with all due respect to their newer counterparts.

HTTPS

Of all the things that can be done to make your API, more secure, requiring it to run over HTTPS is the easiest thing to implement. This does nothing for authorization and authentication – which I will get to in a moment. HTTPS however, does afford encryption that is not present in regular HTTP traffic. With HTTPS, your traffic is far less likely to be subject to packet sniffing eavesdropping attacks. The key is, how can you ensure that your Web API is only accessed via HTTPS? The answer lies in one of the most useful features in ASP.NET MVC – that has also been implemented in ASP.NET Web API: Action Filters. And in this case, I’ll make the action filter a global action filter – eliminating the need to manually add the filter to every controller action.

Here is the custom action filter

using System;
using System.Linq;
using System.Net.Http;
using System.Web.Http.Filters;
using System.Web.Http.Controllers;

namespace WebAPI
{
 public class CustomHttpsAttribute : ActionFilterAttribute
 {
  public override void OnActionExecuting(HttpActionContext actionContext)
  {
   if (!String.Equals(actionContext.Request.RequestUri.Scheme, "https", StringComparison.OrdinalIgnoreCase))
   {
    actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest)
    {
     Content = new StringContent("HTTPS Required")
    };
    return;
   }
  }
 }
}

Nothing very complicated going on here. If https is not present in the URL, a response is fabricated indicating a bad request (error 400). A message is also added to the response content as well.

As the previous figure illustrates, when we attempt to navigate to the Web API with HTTP, as opposed to HTTPS, a response with status code 400 results – along with a friendly message in the response body that describes the error.

Tokens based on Public/Private Keys

Of all the ways to authorize and authenticate, it seems to me that tokens have done a good at this task. A token can be used to authorize, and for the most part, authenticate a user. Of course, it is possible for a key to get passed around. I’ll address that in the next section. That said, it may be perfectly fine to assume that if a visitor has a properly encrypted token, that visitor is who he/she/it claims to be (in cases where its an application, not a person, it is the right pronoun!!)

The idea behind a private and public key pair is simple. A token is encrypted with the public key. In some cases, the public key sits in the field. In cases where data has to be encrypted by the client to be decrypted on the server, the public key needs to be in the field. The server is the only thing that should have the private key. It’s the private key that handles the decryption. To get this working, we need a simple class to handle our encryption needs:

using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace WebAPI
{
 public class RSAClass
 {
  private static string _privateKey = "<RSAKeyValue><Modulus>s6lpjspk+3o2GOK5TM7JySARhhxE5gB96e9XLSSRuWY2W9F951MfistKRzVtg0cjJTdSk5mnWAVHLfKOEqp8PszpJx9z4IaRCwQ937KJmn2/2VyjcUsCsor+fdbIHOiJpaxBlsuI9N++4MgF/jb0tOVudiUutDqqDut7rhrB/oc=</Modulus><Exponent>AQAB</Exponent><P>3J2+VWMVWcuLjjnLULe5TmSN7ts0n/TPJqe+bg9avuewu1rDsz+OBfP66/+rpYMs5+JolDceZSiOT+ACW2Neuw==</P><Q>0HogL5BnWjj9BlfpILQt8ajJnBHYrCiPaJ4npghdD5n/JYV8BNOiOP1T7u1xmvtr2U4mMObE17rZjNOTa1rQpQ==</Q><DP>jbXh2dVQlKJznUMwf0PUiy96IDC8R/cnzQu4/ddtEe2fj2lJBe3QG7DRwCA1sJZnFPhQ9svFAXOgnlwlB3D4Gw==</DP><DQ>evrP6b8BeNONTySkvUoMoDW1WH+elVAH6OsC8IqWexGY1YV8t0wwsfWegZ9IGOifojzbgpVfIPN0SgK1P+r+kQ==</DQ><InverseQ>LeEoFGI+IOY/J+9SjCPKAKduP280epOTeSKxs115gW1b9CP4glavkUcfQTzkTPe2t21kl1OrnvXEe5Wrzkk8rA==</InverseQ><D>HD0rn0sGtlROPnkcgQsbwmYs+vRki/ZV1DhPboQJ96cuMh5qeLqjAZDUev7V2MWMq6PXceW73OTvfDRcymhLoNvobE4Ekiwc87+TwzS3811mOmt5DJya9SliqU/ro+iEicjO4v3nC+HujdpDh9CVXfUAWebKnd7Vo5p6LwC9nIk=</D></RSAKeyValue>";
  private static string _publicKey = "<RSAKeyValue><Modulus>s6lpjspk+3o2GOK5TM7JySARhhxE5gB96e9XLSSRuWY2W9F951MfistKRzVtg0cjJTdSk5mnWAVHLfKOEqp8PszpJx9z4IaRCwQ937KJmn2/2VyjcUsCsor+fdbIHOiJpaxBlsuI9N++4MgF/jb0tOVudiUutDqqDut7rhrB/oc=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
  private static UnicodeEncoding _encoder = new UnicodeEncoding();

  public static string Decrypt(string data)
  {
   var rsa = new RSACryptoServiceProvider();
   var dataArray = data.Split(new char[] { ',' });
   byte[] dataByte = new byte[dataArray.Length];
   for (int i = 0; i < dataArray.Length; i++)
   {
    dataByte[i] = Convert.ToByte(dataArray[i]);
   }

   rsa.FromXmlString(_privateKey);
   var decryptedByte = rsa.Decrypt(dataByte, false);
   return _encoder.GetString(decryptedByte);
  }

  public static string Encrypt(string data)
  {
   var rsa = new RSACryptoServiceProvider();
   rsa.FromXmlString(_publicKey);
   var dataToEncrypt = _encoder.GetBytes(data);
   var encryptedByteArray = rsa.Encrypt(dataToEncrypt, false).ToArray();
   var length = encryptedByteArray.Count();
   var item = 0;
   var sb = new StringBuilder();
   foreach (var x in encryptedByteArray)
   {
    item++;
    sb.Append(x);

    if (item < length)
     sb.Append(",");
   }

   return sb.ToString();
  }
 }
}

In this simple RSAClass, I have embedded the public/private RSA Parameters. To create and export new parameters, you would issue the following code outlined in the next image:

[Test]
 public void RSAParameters() {

  var rsa = new RSACryptoServiceProvider();
  var privateParameters = rsa.ExportParameters(true);
  var publicParameters = rsa.ExportParameters(false);
  
  //Export private parameter XML representation of privateParameters
  //object created above
  Debug.WriteLine(rsa.ToXmlString(true));

  //Export private parameter XML representation of publicParameters
  //object created above
  Debug.WriteLine(rsa.ToXmlString(false));
 }

The following is a simple test that illustrates how to encrypt and decrypt a string with the public/private key pair:

[Test]
  public void VerifyToken() {
  
   var token = "User1";
   var encryptedToken = RSAClass.Encrypt(token);
   var decryptedToken = RSAClass.Decrypt(encryptedToken);

   Assert.AreEqual(token,decryptedToken);

  }

In this case, the encrypted 128 byte token that results for “User1″ is:

57,46,60,70,93,230,85,33,98,19,10,46,84,91,218,43,207,42,159,
167,5,25,157,4,224,142,235,8,160,199,123,100,107,58,37,204,133,
81,138,196,237,190,56,119,158,7,224,89,84,85,208,169,44,179,102,
218,55,60,76,134,144,22,208,230,165,179,83,125,86,57,224,42,
29,58,188,45,73,33,160,87,165,105,131,139,132,137,209,67,92,36,
168,73,176,205,251,48,240,228,14,39,197,36,42,21,216,242,172,
4,160,234,138,77,156,28,191,63,111,207,221,31,103,213,58,62,
186,123,221,230

You may wish to modify this by having a smaller encrypted token. This is but one approach of many that are possible.

The next thing we need is an action filter to make sure the token is present in each and every request:

using System;
using System.Linq;
using System.Net.Http;
using System.Web.Http.Filters;
using System.Web.Http.Controllers;
using WebAPI.Models;

namespace WebAPI
{
 public class TokenValidationAttribute : ActionFilterAttribute
 {
  public override void OnActionExecuting(HttpActionContext actionContext)
  {
   string token;

   try
   {
    token = actionContext.Request.Headers.GetValues("Authorization-Token").First();
   }
   catch (Exception)
   {
    actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest)
    {
     Content = new StringContent("Missing Authorization-Token")
    };
    return;
   }

   try
   {
    AuthorizedUserRepository.GetUsers().First(x => x.Name == RSAClass.Decrypt(token));
    base.OnActionExecuting(actionContext); 
   }
   catch (Exception)
   {
    actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden)
    {
     Content = new StringContent("Unauthorized User")
    };
    return;
   }
  }
 }
}

There are two levels of checking here. The first is whether or not the authorization token is present in the header. If not, a bad request response (error 400) is returned. If the token was provided but does not resolve to an authorized user, then an unauthorized response (error 401) is returned. The following illustrates how to setup Fiddler with the Authorization-Token in the header:

The following illustrates the results of the call in the previous image:

For purposes of this demo, I established a simple authorized user repository that has a list of pre-defined users:

using System;
using System.Collections.Generic;
using System.Linq;

namespace WebAPI.Models
{
 public class AuthorizedUserRepository
 {
   public static IQueryable<User> GetUsers() {
   
      IList<User> users = new List<User>();   
      users.Add(new User("User1"));
      users.Add(new User("User2"));
      users.Add(new User("User3"));
      users.Add(new User("Administrator"));
   
      return users.AsQueryable();
   }
 }
}

Within this approach, there are all sorts of variants you could employ. For example, you could swap out the public/private keys on some periodic basis. Of course, that means you will have to issue new tokens to users who have access to your API!!

IP Filtering

Up to this point, while the system is pretty secure, it can be locked down further. Right now, tokens could be moved from location to location. It may be that you only want to authorize traffic from certain IP addresses and hosts. For purposes of this demo, I created a simple Authorized IP Repository:

using System;
using System.Collections.Generic;
using System.Linq;

namespace WebAPI.Models
{
 public class AuthorizedIPRepository
 {
    public static IQueryable<string> GetAuthorizedIPs() {
    
      var ips = new List<string>();

      ips.Add("127.0.0.1");
      ips.Add("::1");
      
      return ips.AsQueryable();
    }
 }
}

In actual practice, I might take the extra step of linking specific tokens to a specific IP range. I’ll leave that exercise to you! The following is the action filter that restricts traffic from a defined IP list:

using System;
using System.Linq;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using System.Net.Http;
using WebAPI.Models;

namespace WebAPI
{
 public class IPHostValidationAttribute : ActionFilterAttribute
 {
  public override void OnActionExecuting(HttpActionContext actionContext)
  {

   var context = actionContext.Request.Properties["MS_HttpContext"] as System.Web.HttpContextBase;
   string userIP = context.Request.UserHostAddress;
   try
   {
    AuthorizedIPRepository.GetAuthorizedIPs().First(x => x == userIP);
   }
   catch (Exception)
   {
    actionContext.Response =
       new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden)
       {
        Content = new StringContent("Unauthorized IP Address")
       };
    return;
   }
  }
 }
}

To make these action filters global, the following code in the Global.asax Application_Start() will do the trick:

var config = GlobalConfiguration.Configuration;
config.Filters.Add(new TokenValidationAttribute());
config.Filters.Add(new CustomHttpsAttribute());
config.Filters.Add(new IPHostValidationAttribute());

If you wished to initiate a request from your C# code, you might have a utility method like this:

  WebRequest getRequest(string method, string contentType, string endPoint)
  {
   var request = WebRequest.Create(endPoint);
   request.Method = method;
   request.ContentType = contentType;

   ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

   request.Headers.Add("Authorization-Token", "57,46,60,70,93,230,85,33,98,19,10,46,84,91,218,43,207,42,159,167,5,25,157,4,224,142,235,8,160,199,123,100,107,58,37,204,133,81,138,196,237,190,56,119,158,7,224,89,84,85,208,169,44,179,102,218,55,60,76,134,144,22,208,230,165,179,83,125,86,57,224,42,29,58,188,45,73,33,160,87,165,105,131,139,132,137,209,67,92,36,168,73,176,205,251,48,240,228,14,39,197,36,42,21,216,242,172,4,160,234,138,77,156,28,191,63,111,207,221,31,103,213,58,62,186,123,221,230");

   return request;
  }

This request has the necessary information to make an API request

That’s pretty much it. Now we have a ASP.NET Web API that requires requests to be under the HTTPS protocol, requires an encrypted authorization token and requires traffic to only come from a predefined population of IP addresses. There are any number of ways you can vary this implementation. My goal with this post was to make it easy to get started. With this approach, you don’t have to worry about cumbersome query string parameters, etc. In this example, I am putting authentication details where they belong – in the header – which means it will work for anything. No need to deal with clumsy query string parameters either.

Enjoy!!

About johnvpetersen

I've been developing software for 20 years, starting with dBase, Clipper and FoxBase + thereafter, migrating to FoxPro and Visual FoxPro and Visual Basic. Other areas of concentration include Oracle and SQL Server - versions 6-2008. From 1995 to 2001, I was a Microsoft Visual FoxPro MVP. Today, my emphasis is on ASP MVC .NET applications. I am a current Microsoft ASP .NET MVP. Publishing In 1999, I wrote the definitive whitepaper on ADO for VFP Developers. In 2002, I wrote the Absolute Beginner’s Guide to Databases for Que Publishing. I was a co-author of Visual FoxPro Enterprise Development from Prima Publishing with Rod Paddock, Ron Talmadge and Eric Ranft. I was also a co-author of Visual Basic Web Development from Prima Publishing with Rod Paddock and Richard Campbell. Education - B.S Business Administration – Mansfield University - M.B.A. – Information Systems – Saint Joseph’s University - J.D. – Rutgers University School of Law (Camden) In 2004, I graduated from the Rutgers University School of Law with a Juris Doctor Degree. I passed the Pennsylvania and New Jersey Bar exams and was in private practice for several years – concentrating transactional and general business law (contracts, copyrights, trademarks, independent contractor agreements, NDA’s, intellectual property and mergers and acquisitions.).
This entry was posted in ASP.NET, ASP.NET MVC 4. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Pingback: How to: How to secure an ASP.NET Web API | SevenNet

  • Pingback: Basic Authentication with a Guid token for REST api instead of username/password | Ngoding

  • Pingback: Pierre-Henri Nogues - Développeur .Net | Liens intéressants .Net

  • Pingback: Any secure ASP.NET Web API Authentication for desktop app clients closed | Code and Programming

  • Anonymous

    Hi Ryan,

    Thanks for the comment.

    You nailed it with this aspect of your comment:
    **
    Now, I would agree that oAuth is complicated and requires an interaction with another party,
    **

    That was my point in saying it “really”does not work all that well. I could have and should have been more artful in the way I phrased that. The fact is, technically, it (oAuth) can and does work. OpenID definitely represents an impedance mismatch between what WebAPI’s require and do and how OpenID works.

    I will be re-working this example to incorporate oAuth (google, twitter, etc). What I don’t want lost is the point on business practicality. If your API is something that sits out in the public (like the Bing Maps API for example) – then sure – oAuth can work. But I’ll tell you – I like the simplicity of sending an API Key!! Can the key get stolen? Sure, but if you send it in a header under https – that is not likely to happen. Also, you can renew/re-gen the key’s. That, with a combination of IP sourcing, the likelihood of getting spoofed goes way down.

    On the other hand, what if yours is a private API – behind your DMZ? Or, it’s reach is limited? In that case, Windows Authentication (in the case of a Windows Server) could certainly work. I certainly would not expect a private enterprise to defer to some third party like Google, Twitter, etc.

    And finally, what if those third parties or some other entity is down? I like the idea of API’s being self-sufficient.

    That’s where I was coming from. Your point on the technical applicability of oAuth is well taken and I will expand the example to include that.

    Thanks,

    JVP

  • http://twitter.com/ryanwi Ryan Williams

    I’m really confused by this statement, “For many applications, OpenID or oAuth can work fine. But for a Web API, it really does not work.”

    oAuth is designed specifically for API usage while OpenID fits more into what you were saying as far as not working well for Web APIs. By combining the two and claiming that oAuth doesn’t work is a dangerous statement. It is pretty much the standard for API authorization. Plus, it’s based on tokens as you go on to write about.

    Now, I would agree that oAuth is complicated and requires an interaction with another party, so from that standpoint it really depends on the use of the API as to whether to use oAuth.

  • http://www.abercrombieandfitchonsale.co.uk/ Beverly

    Your site provided us with valuable information to work on.You have done a marvellous job!

  • Anonymous

    Good points. Indeed, we could easily flip things and have the server decrypt with the user’s public key. The server app could still generate these keys and make them available to the user.

  • naim_akbar

    Hi Jon,

    Thanks for your reply. I think this post is slightly confusing the idea of a public/private key pair (based on the concept of public key encryption).

    The whole idea of a public key is that it is public. Anyone can use it. So if for example I am sending a message to a User called ‘X’, then i’d encrypt the message with ‘X’s public key, knowing that X is the only one who can read it(decrypt it).. using his private key.

    Otherwise, based on what I have read in the post (and ignoring the IP checking you mentioned), you could have 2 users, encrypt their token with the same key …as its public, and the server would not be able to distinguish between the two users.

    Digital signatures work the other way. With this approach, you’d sign the token (or encrypt) with your private key….and only you know this key.. and the sever could verify (decrypt) the token by using your public key. This way, the sever can be sure that the token has come from you (origin authentication).

    What would make more sense is using the concept of ‘secret key encryption’. This is where both the client and server share the same secret. Therefore you still get origin authentication.

  • Anonymous

    You can generate the keys when you register the user. You could generate new ones on some perioidic basis. The user would need the public key so that they could encrypt their user id. In this example, I used the user id as the thing that was encrypted. You could use something else if you wanted. For example, let’s say you encrypt a json string that has your user id and a time stamp (based on utc – ut0). You could set up the API to only accept a request that has a timestamp that is no older than 1 minute. That’s pretty secure since it would require the user to have the public key. That’s an assumption you have to make. You could limit the requests for that user id to a specific set of IP addresses. In my opinion, that would be secure enough. Even if some malicious user got the key and encrypted, they are not in your DMZ – and hence would have a different IP address. That would be pretty hard to spoof. There are all kinds of variants within the approach. Regardless of which one you choose, the same basic mechanisim remains the same.

  • Anonymous

    Hi Ken,

    You add it to the request header. I named it Authorization-Token. The API examines the request and checks the headers collection to make sure Authorization-Token is present. Look at the tests in the example code. You will see the factory method that creates a new request. Part of that process adds the Authorization-Token to the headers collection.

  • http://twitter.com/AzZuM Ken deKlerk

    John, relating to your other posts, how would you add the key to every request when you consume a rest api?

  • naim_akbar

    When do we generate these public private keys used for encrypting/decrypting tokens for a particular user. Would this be done on registration?

    Maybe I have misunderstood the post, but what level of security is this. If the token is based on a username, then whats to stop a malicious user to use the same username and hence masquerade as this user. Is it down to the public/provate key pair? I guess it would be.

    If this is the case how would I as a user have access to my private key when I am signing the token ? Where do I get it from

  • http://twitter.com/anthonyyates anthonyyates

    Hi Cecil,

    I’ve used http://code.google.com/p/crypto-js/ for simple stuff and can recommend it.

  • Pingback: Distributed Weekly 149 — Scott Banwart's Blog

  • http://twitter.com/sidecut James Raden

    Null, unless T in FirstOrDefault is bool, in which case the default value *is* false. Slip of the tongue, I’m sure.

  • Anonymous

    The next post converts the attributes to message handlers. Thanks again Pedro for the comment!!

    I probably could bypass controllers completely!!

  • Anonymous

    As it turns out, you do have to wrap the Headers.GetValues call in a try catch block. If no authorization header is passed, the code never gets to the linq query First or FirstOrDefault.

  • Anonymous

    That is an excellent point. I’ll look into that. As an mvc guy, I still have controllers on the mind!!

    Thanks for the comment.

  • Anonymous

    Returns false or null?

  • Chris Martin

    Let me preface this by saying that I have no dog in this fight. :D

    FirstOrDefault does NOT swallow exceptions. If nothing matches the predicate it returns false.

  • http://pedroreys.com/ Pedro Reys

    John,

    Why did you choose to create Action Filters instead of HttpMessageHandlers?

    The type of things you are doing are more related to the Http Request Message itself, so makes more sense to me to use message handlers instead of filters. Also, using message handlers allow you to short circuit the message processing and return before even getting into the Controller Dispatcher.

  • http://twitter.com/cecilphillip Cecil L. Phillip

    Got it. Great post and I appreciate your responses.
    Thanks

  • Anonymous

    In this case, insofar as the content is concerned, I’m letting https handle that security. But assuming I wanted to encrypted the payload with a private key, then I would want some intermediate service (the true client to the rest service) to handle that task. Then, as far as your client is concerned (which only communicates with the intermediate service behind your dmz) – it’s all unencrypted – and would be secure since you are behind your firewall. I’m sure there are js based libraries, that with a public key, you could encrypt. BUT – you would only have a public key. You typically don’t decrypt with a public key – only do so with the private key. Again – relying on https to handle the transport issues. My goal in this post was to offer a simple way to authorize/authenticate – in a secure way – and at the same time – be simple and not burdensome.

    HTH

  • Anonymous

    The token you would have would already be encrypted. The server handles the decryption – with the private key. Once the user id has already been encrypted with the public key, there’s no reason to keep doing that. Remember, this is a rest service, so each call has to be authenticated/authorized – and that is getting handled with the token.

    HTH

  • http://twitter.com/cecilphillip Cecil L. Phillip

    I was looking at it from the perspective of encryption algorithm performance on the client. Do you have a sample or recommended library to handle encryption/decryption of the pay load in JavaScript?

  • Gene Reddick

    I guess what I’m really asking is how to pass in the authenticated userId, so you don’t have to do a second look up in the API action to confirm the userId and the token match?

    I’m assuming in my case that I can’t just use the userId as the basis for the token.

    For example, with the web api inside of a web project that handles authentication, I can (somewhat clumsily) call:

    var principal = ControllerContext.Request.GetUserPrincipal();
    if(!principal.Identity.IsAuthenticated) {
    throw new HttpResponseException(HttpStatusCode.Unauthorized);
    }
    var userName = principal.Identity.Name;

    I suppose I could pass the userId in the header and then validate that in addition to the token in the TokenValidationFilter, then read it in the action method now that I know that both userId and token match?

    Or is there a better way to handle this?

  • Anonymous

    Not sure I understand the question. If you are talking about an Ajax call, then in the beforeSend handler, you would take care of adding the header. In practice, I would likely never do that – especially if my app was consuming the services of the Web API. Instead, I’d having something on my server that hid the details from the browser. That said, there’s nothing stopping you from making an Ajax call – something like $.ajax() – and then building up the request and then processing the results.

  • Anonymous

    In my example, I pass it in the header. But, you could modify things and pass it as a QueryString Parameter. You would want to use something smaller than the byte array I used here…:-) Does that help?

  • Anonymous

    “it is considered bad form to use try/catch blocks for control flow …”

    Says who? Conventional Wisdom? I see this sort of thing getting tossed around like “Best Practice” or “Bad Form”. Usually, folks can’t cite to a single authority to really substantiate that conclusion.

    Now…let’s get to the use of FirstOrDefault. Essentially, that gobbles up the exception handling. There is an implicit try catch in there. Regardless of which approach, I would have to check the value. Whether I check for null (with an if statement) – or I let a try catch handle it (using just First) – I don’t see the big difference. If you are going to talk about readility, that Chris – is purely a matter of preference and opinion. What reads well for one may not read well for another.

    As to performance, I would seriously question whether there is any performance impact here – especially given what FirstOrDefault actually does under the covers.

    The real issue is how cohesive the code is – how well it conforms to SOLID principles. In this case, single responsibility applies and that is all a filter should do…just one thing.

    It’s clear from my code why a 400 would be returned as well. If there is one flaw, it’s that I only have one exception block. What about cases when another exception is at hand. I probably should handle that differently and that is a point I think you were making (even though your code sample has no exception handling whatsoever.. :-)).

    When a token can’t be found – that is an exception in my API – and it’s an exception I handle. Some may choose to deal wit the exception implicitly (via FirstOrDefault) or explcitely like I did). Reasonable people can disagree as to readability. FirstOrDefault has a cost – just like exception handling. If you are going to cite performance, you are oblgiated to confront that topic.

  • Gene Reddick

    If you wanted to know which user’s token had been used, how would you pass that to the api action method?

  • Anonymous

    The goal was to make it simple. I had to at least mention oAuth and OpenID. Your points are well taken, but with all due respect, had I gone down the approach you suggested, I would not have met my goal. The title is about securing your Web API. My goal was to explain how to make that happen in a simple, yet robust way. I did that. Cookies, CPU overhead, etc – all stuff that is valid in the discussion – but nothing that I have brought up here.

    I completely disagree that Authentication is a topic on its own – at least to the extent that it did not belong in this post. You have to consider both authentication and authorization when discussing how to secure a Web API.

    You are right – securiing a Web API is a complex problem – but it does not have to be unecessarily complex. IMO, some of the items in your comment go down an uncessarily complex road. In fact, most of the discussions I’ve seen are too complex – which is why I created the post.

    Indeed, I could easily expand the appraoch to encrypt the payload and to incorporate different approaches to passing a token around. As for bing maps, etc – I’m familiar wtih those approaches. But – I didn’t want to get into query strings. Could I modify my approach to do something like that? Sure. And it would be fairly easy to do that. It probably makes sense to also allow the passing of a smaller encrypted token to do just that. Much of that boils down to how your API will be consumed. For Bing/Google Maps, that approach makes perfect sense.

    As to what you would split up or what you would have started with – all I will suggest is that you are free to make a blog post of your own…. :-)

    JVP

  • Chris Staley

    Nice post.

    However, it is considered bad form to use try/catch blocks for control flow for a number of different reasons (e.g. readability, performance, and unexpected exceptions). For example, to test for the presence of the authorization token in TokenValidationAttribute it’s considered better to do something like this:

    var token = actionContext.Request.Headers.GetValues(“Authorization-Token”).FirstOrDefault();
    if (token == null) {
    actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest)
    {
    Content = new StringContent(“Missing Authorization-Token”)
    };
    return;
    }

    It’s clearer from that code why a 400 would be returned by the filter.

  • http://twitter.com/cecilphillip Cecil L. Phillip

    How would JavaScript clients handle encryption in browser environments? Encryption is pretty intensive in browsers … isn’t it ?

  • http://twitter.com/bartczernicki Bart Czernicki

    I would have split the article up…you start talking about Authentication and Authorization..OAuth and then jump to HTTPS?

    First you want to start off talking about private or public APIs…huge difference.

    I would have then started with Identification. That is a public API key concept…Bing Maps or Azure Storage where API Keys identify the subscriber of the API. That is a concept all on its own…IP filterting and API throttling etc can all be discussed. Public API Keys (can be) and usually are shareable (Google, Azure APIs etc)

    Authentication is a completely different topic on its own. There are ways to pass derived/encrypted tokens around. HMAC is a great example..no private key needed its a Hash + Salt match with a timespan. Login cookies as well… While talking about private/public keys you mention Authorization….this has nothing to do with Authorization/data level security…that is usually up to the Controller actions or business logic to determine (roles, users etc.)

    Then you have your transport layer…HTTPs/TLS. You can also add in here encrypting the payload of the actual HTTP request/response whether its JSON/XML. This allows you to have the flexibility of an HTTP service and only encrypting select sensitive data and not take the CPU overhead, complexity of adding HTTPs or “some of” the HTTPs caching pitfalls in older browsers etc.

    I point these out, because securing your Web API layer is much more complex and needs to be thought out in a layered approach.

  • Anonymous

    Thank you. Certainly, WIF would work. The underpinnings of that, like my solution, is a public/private key. WIF is a viable approach, albeit a bit more complicated. I think that would make a nice blog post too… hint hint… ;-)

  • Guest

    Nice article. And a straightforward approach. But have you looked at WIF and it’s support for Simple Web Tokens? Wouldn’t that work just as well?

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1078