AuthorizeAttribute    Posted:


I found scarce good examples of authorization when using .NET Web Api that I thought it would be a good idea to put up my own, in case I need it in the future. Blogging about it makes it easier to remember how it worked. Most of the examples I could find where just rehashes of the same MSDN example project.

In a project I'm currently working on I wanted to provide both user login access and an "api-key" type of access to my controllers. The following code shows how I did it. I subclassed System.Web.Http.AuthorizeAttribute and overrode the IsAuthorized-method. It looks for the Authorization header in the client request and attempts to authenticate using it. If it fails, it failovers to the original authorization scheme.

TokenAuthorize.cs

 1 public class TokenAuthorize : AuthorizeAttribute
 2 {
 3         private static readonly log4net.ILog log = log4net.LogManager.GetLogger("TokenAuthorize");
 4 
 5         public TokenAuthorize()
 6                 : base()
 7         {
 8         }
 9 
10         /// <summary>
11         /// Safe against timing attacks.
12         /// </summary>
13         /// <param name="cmpAgainst"></param>
14         /// <returns></returns>
15         protected bool SafeCmp(String cmpAgainst)
16         {
17                 var password = ConfigurationManager.AppSettings["BasicAuthAPIKey"];
18 
19                 if (cmpAgainst.Length != password.Length)
20                         return false;
21 
22                 // This loop will never short-circuit. Therefore it will always take the
23                 // same amount of time.
24                 int result = 0;
25                 for(int i = 0; i < cmpAgainst.Length; i++) {
26                         result |= ((byte)cmpAgainst[i]) ^ ((byte)password[i]);
27                 }
28                 return result == 0;
29         }
30 
31         protected override bool IsAuthorized(HttpActionContext httpContext)
32         {
33                 // Check for token in basic authentication
34                 var auth = httpContext.Request.Headers.Authorization;
35                 if(auth != null && auth.Scheme == "Basic") {
36                         try
37                         {
38                                 var nonbase64 = Convert.FromBase64String(auth.Parameter);
39                                 var asStr = Encoding.ASCII.GetString(nonbase64);
40 
41                                 log.Debug("Authorization attempt using token: " + asStr);
42                                 return (SafeCmp(asStr));
43                         }
44                         catch (FormatException)
45                         {
46                                 log.Debug("Invalid base64 format in auth: "+auth.Parameter);
47                         }
48                 }
49 
50                 // Check for cookie as usual
51                 return base.IsAuthorized(httpContext);
52         }
53 }

In addition, I added the API key as an appSetting value to my web.config file:

<configuration>
        <!-- ... -->
        <appSettings>
                <!-- ... -->
                <add key="BasicAuthAPIKey" value="<password>"/>
        </appSettings>
        <!-- ... -->

Then put the [TokenAuthorize] attribute on any controller which needs authorization. E.g.

[TokenAuthorize]
public class ProductController : ApiController
{
        // ....
}

Now you can authorize via the secret API key supplied in the Authorization header or the normal session/cookie way.

Important to note here that I overrode the System.Web.Http version and not the System.Web.Mvc version which has the same name but different method signatures. Also, for future reference it seems you should use .Http for Web Api and .Mvc for MVC controllers. Otherwise it doesn't seem to work.