Custom AuthorizeAttribute Not Working in ASP.Net MVC4?

As you may have guessed from my other recent blog posts and tweets, I’m working on a web based project.  Actually, I’m working on an updated version of Pylons, in preparation for creating a mobile client.

Since I am reworking my API to allow mobile clients to connect to it, that means I can no longer use the standard built in forms authentication.  The reason for this is that you don’t necessarily have cookie support the same way with a mobile client as you do with a web page running on a desktop.  Besides, the mobile version of Pylons isn’t going to be running in a web page, it’s a mobile app.   I knew this from the start, so I knew eventually that I’d have to switch over to using Basic HTTP authentication.  This means, that rather than having authentication details stored in a cookie, they are passed in with each call to the service in the HTTP header.

The MVC 4 framework doesn’t support this sort of authentication out of the box.  Instead, it is necessary to roll your own.  There’s a ton of blog posts out there on how to do this.  I based my approach on this one: http://kevin-junghans.blogspot.ca/2013/02/mixing-forms-authentication-basic.html.  Kevin’s approach nails all the bits and pieces I was needing – Basic HTTP authentication, and a decent approach of using a custom role provider.  (Since Pylons is only loosely based on the MVC 4 framework, it has it’s own way of doing roles via a custom RoleProvider.  Perhaps a topic for another post.)

I downloaded Kevin’s sample, and it worked great.  I did a decent job of mocking it up in my own project, but for the life of me couldn’t get it working.  No matter what I did, the OnAuthorize function was never being called.  It didn’t matter what I did, I could never get it to fire.  It even got to the point where I put an exception in the custom authorize attribute constructor, just to see if it was being created.  It was.  That meant that somewhere, something in the bowels of my web API was overriding my custom attribute.  

In my API, I had two test methods – one that was decorated with the [AllowAnonymous] attribute and another with the [BasicAuthorize] attribute.  The method with the [AllowAnonymous] always worked, and was always accessible, whether I was logged in or not.  The method with the [BasicAuthorize] attribute never worked – it would always return a 401 Unauthorized.  Again, because I knew it was at least hitting the constructor of the BasicAuthorizeAttribute class, I knew it was at least using the correct attribute.  At this point (after several hours of fruitless searching, and spending way more time than I had planned on it), I figured there must be something else in my particular web API that was somehow messing with the authentication.  

In the previous version of Pylons, I had it set so that any method by default required authentication.  This makes sense from a security standpoint, as you want to make sure that things are locked down, and only open in places that you want them open.  This can be accomplished by setting a filter in the WebApiConfig Register method.  In this method, you can set various settings for things like routing, exception handling, model validation, etc.  Low, and behold… what did I find?

 

 //Enable authorization
config.Filters.Add(new AuthorizeAttribute());

Well, that’d do it.  It turns out that a little copy-pasta from the previous version of Pylons came back around to bite me.  This was basically the same as applying the standard [AuthorizeAttribute] to all my methods, except those that were marked with [AllowAnonymous].  This explains why the constructor of the BasicAuthorizeAttribute was being called, but never the OnAuthorize function.  If the [AuthorizeAttribute] was the first to be checked, it would always fail, as I’m no longer using cookie based authentication.  Since it failed, there’d be no point in checking additional authorization attributes, as if the user isn’t authorized in one place, they shouldn’t be authorized at all.

Moral of the story: Take your time, debug things slowly and logically, and be wary of copy-paste!

 

 

Advertisements

One comment

  1. Marcin Habuszewski

    Whoa this was a real lifesaver. I had a very similar problem. A colleague of mine added such global filter to the project (about which I did not know) and then I spent hours trying to figure out why my custom filter doesn’t work…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s