API Key Authorization Through Query String In ASP.NET Web API AuthorizationFilterAttribute

We will see how API key authorization (verification) through query string would be implemented In ASP.NET Web API AuthorizationFilterAttribute
2012-03-05 16:23
Tugberk Ugurlu


Update on the 29th of June, 2012:

The nuget package I use inside this post is not compatable with ASP.NET Web API RC and will not be with ASP.NET Web API RTM. I have another package named WebAPIDoodle which has the same funtionality as here. The source code for WebAPIDoodle: https://github.com/tugberkugurlu/WebAPIDoodle

Update on the 17th of October, 2012:

Confession

@SoyUnEmilio has pointed out a very good topic in his comment and I replied back to him but I would like to make it clear inside the post itself, too.

I wrote this article at the early stages of the framework and I now think that I mixed the concepts of authantication and authorization a lot. So, this blog post does not point you to  a good way of implementing authantication and authorization inside you ASP.NET Web API application. I don't want to delete the stuff that I don't like anymore. So, this blog post will stay as it is but I don't want to give the wrong impression as well. As the framework is now more mature, my thoughts on authentication and authorization is shaped better.

In my opinion, you should implement your authantication through a message handler and you almost always don't want to try to perform authorization inside that handler. You may only return "Unauthorized" response inside the message handler (depending on your situation) if the user is not authanticated at all but that should be futher than that. Here is a message handler sample for the API Key authentication: ApiKeyAuthenticationHandler.cs.

As for the authorization part, it can be handled by the System.Web.Http.AuthorizeAttribute. The AuthorizeAttribute checks against the Thread.CurrentPrincipal. So, the principal that you have supplided inside your message handler will be checked against. You also have a change to perform role or user name based authorization through the AuthorizeAttribute.

If you are going to build a REST API, you don’t probably want to expose all the bits and pieces to everyone. Even so, you would like to see who are making request for various reasons. One of the best ways is the API Key verification to enable that.

In a nutshell, I'll try to explain how it works. You give each user an API key (a GUID would be suitable) and ask them to concatenate this key on the request Uri as query string value. This business logic of assigning the keys works like this. But IMO, one thing is important. No matter what you do, do not manage the API key as admin. You need to find a way to make users manage their keys. User should be able to reset their API key whenever they want and by doing that, old key must gets invalid. As much as this part of the process is important, how you very them on your application is another issue.

With ASP.NET Web API, it is pretty easy to intercept a request and change the behavior of that request in any level. For API Key verification, we have two options: 1) Creating a DelegetingHandler and register it as a message handler. 2) Creating an Authorization filter which will be derived from AuthorizationFilterAttribute class. With one of those two ways, we can verify the user according to API Key supplied.

Honestly, I am not sure which one would be the best option. But, it is certain that if you don’t want the whole application to be API key verified, a filter is the best option and it can be applied to an action, controller and globally for entire application. On the other hand, message handlers are involved before the filters. So, that might seem better if you would like your whole app to be API key verified.

I have created an API key verification filter and I tried to make it generic so that it can be applied for all different verification scenarios. Let me show you what I mean.

First of all, go get the bits and pieces through Nuget. The package is TugberkUg.Web.Http and it is a prerelease package for now:

PM> Install-Package TugberkUg.Web.Http -Pre

This package contains other stuff related to ASP.NET Web API. You can check out the source code on https://github.com/tugberkugurlu/ASPNETWebAPISamples/tree/master/TugberkUg.Web.Http/src/TugberkUg.Web.Http.

For the purpose of this post, what we are interested in is ApiKeyAuthAttribute class and IApiKeyAuthorizer interface. The logic works in a very simple way:

We need a class which will be derived from IApiKeyAuthorizer interface.

public interface IApiKeyAuthorizer {

    bool IsAuthorized(string apiKey);
    bool IsAuthorized(string apiKey, string[] roles);
}

As you can see, there are two methods here. First one takes only one parameter which is the API key. This method will be invoked if you try to verify the request only based on API key. Unlike the first one, the second method takes two parameters: API key as string and roles as array of string. This one will be invoked if you try to verify the request based on API key and roles.

I have created an in memory API key authorizer to try this out:

public class InMemoryApiKeyAuthorizer : IApiKeyAuthorizer {

    private static IList<User> _validApiUsers = new List<User> { 

        new User { ApiKey = "d9c99318-53b6-4846-8613-e5aecb473066", 
            Roles = new List<Role>() { 
                new Role { Name = "Admin" } 
            }
        },
        new User { ApiKey = "dd97a5aa-704e-4c9e-9bd5-5e2828392eee", 
            Roles = new List<Role>() { 
                new Role { Name = "Customer" } 
            }
        },
        new User { ApiKey = "b2e684d7-8807-4232-b5fc-1a6e80c175c0", 
            Roles = new List<Role>() { 
                new Role { Name = "Admin" } 
            }
        },
        new User { ApiKey = "36171dc0-4925-4b12-a162-0d6d193acb75" },
        new User { ApiKey = "c8028fae-4887-4e91-8fa5-9655adae6ec1" },
        new User { ApiKey = "c4bdb227-095a-4fde-8db5-1c96d86e897a" },
        new User { ApiKey = "ff10e537-44d5-49b3-add2-6011f54de996" },
        new User { ApiKey = "3dcd18cf-e373-4436-9171-aa7f20dae23c" },
        new User { ApiKey = "17b2663d-df81-4f63-b10e-5ed918a920cf" },
        new User { ApiKey = "44fffbf2-8b32-4c4c-834a-518dd0279efa" }
    };

    public bool IsAuthorized(string apiKey) {

        return
            _validApiUsers.Any(x => x.ApiKey == apiKey);
    }

    public bool IsAuthorized(string apiKey, string[] roles) {

        if(_validApiUsers.Any(x => 
            x.ApiKey == apiKey && x.Roles.Where(r => 
                roles.Contains(r.Name)).Count() > 0)) {

            return true;
        }

        return false;
    }
}

Here, normally you would look at the supplied information about the request and return either true or false. As you can imagine, the request will be verified if true is returned. If not, then we will handle the unauthorized request in a specific way. We will get to there in a minute.

I am not sure these two parameters enough to see if the request is legitimate or not. I had a chance to easily supply the System.Web.Http.Controllers.HttpActionContext as parameter to these methods but I didn’t. Let me know what you think.

Now we have our logic implemented, we can now apply the filter to our application. As you know, filters are attributes and our attribute is as follows:

public class ApiKeyAuthAttribute : AuthorizationFilterAttribute {

    public ApiKeyAuthAttribute(string apiKeyQueryParameter, Type apiKeyAuthorizerType);

    public string Roles { get; set; }

    protected virtual void HandleUnauthorizedRequest(HttpActionContext actionContext);
    public override void OnAuthorization(HttpActionContext actionContext);
    
}

ApiKeyAuthAttribute has only one constructor and takes two parameters: apiKeyQueryParameter as string for the query string parameter name which will carry the API key and apiKeyAuthorizerType as Type which is the type of Api Key Authorizer which implements IApiKeyAuthorizer interface. We also have a public string property called Roles which accepts comma separated list of roles which user needs to be in.

The usage of the filter is simple as follows:

[ApiKeyAuth("apiKey", typeof(InMemoryApiKeyAuthorizer), Roles = "Admin")]
public class CarsController : ApiController {

    public string[] GetCars() {

        return new string[] { 
            "BMW",
            "FIAT",
            "Mercedes"
        };
    }
}

Now, when we hit the site without API Key and with the legitimate user API key which is not under the Admin role, we will get 401.0 Unauthorized response back:

apiKey

This is the default behavior when an unauthorized user sends a request but can be overridden. You need to simply override the HandleUnauthorizedRequest method and implement your own logic.

When we send a request with a proper API key, we will get the expected result:

image

The sample I used here is also on GitHub: https://github.com/tugberkugurlu/ASPNETWebAPISamples/tree/master/TugberkUg.Web.Http/src/samples/ApiKeyAuthAttributeSample

If you have any advice, please comment or fork me on GitHub.



Comments

Tim VanFosson
by Tim VanFosson on Sunday, Mar 18 2012 16:47:23 +02:00

A minor nitpick because it's obviously just an example but you'll limit the throughput of your API by using list searches for users/roles, especially as these grow.  Obviously if they get large enough you'll want to use some persistence mechanism, likely with an in-memory cache.  In either case it would be better to use a Dictionary keyed by APIKey for the users and HashSets for the user's roles.  If your roles are limited you might even want to use enums (flags) and just do a bitwise check.

Tugberk
by Tugberk on Sunday, Mar 18 2012 17:14:41 +02:00

@Tim

Nice advice sir! I used the list here because it is the easiest way of doing it but in a real world scenerio, it requires a litt bit of thought as you indicated.

Justin
by Justin on Monday, Mar 19 2012 06:17:07 +02:00

Excellent article, I was disappointed that when the ASP.NET Web API was announced there was no mention of how to best address security and authorization concerns, this is a good start.

Tugberk
by Tugberk on Monday, Mar 19 2012 09:58:23 +02:00

@Justin

Thanks. Also you may refer to Dominick Baier's blog post series on ASP.NET Web API security and identity model: http://www.leastprivilege.com/CategoryView.aspx?category=IdentityModel

It will be well worth your time.

Jeremy
by Jeremy on Tuesday, May 22 2012 10:53:32 +03:00

Question - if I want to require a public & private key, as well as a list of permissions (rather than Roles), how would I modify the `ApiKeyAuthAttribute` class?  (since your declaration seems to assume some auto-wiring).  I'm guessing something more like http://remy.supertext.ch/2012/04/basic-http-authorization-for-web-api-in-mvc-4-beta/?

 

After reviewing your source code (https://github.com/tugberkugurlu/ASPNETWebAPISamples/blob/master/Tugberk...), I think you just posted the "shorthand" version of your method here, right?  So I will explicitly declare the methods to perform my custom validation.  Just making sure I'm not missing anything.

Tugberk
by Tugberk on Wednesday, May 23 2012 05:03:15 +03:00

@Jeremy

If I understand you right here, you would like to get additional parameters to validate the request. If so, you can create a new Auth filter to do this.

OnAuthorize method of AuthorizationFilterAttribute receives a parameter which is type of HttpActionContext and this parameter gives you the details of the request.

Emre Turan
by Emre Turan on Wednesday, May 30 2012 18:27:02 +03:00

Merhaba,

Çok iyi bir makale olmuş öncelikle tebrik ederim.

Anlamadım bir nokta var. Bu sistem SSL olmayan bir domainde de düzgün çalışır mı?

Saygılar,

Emre.

Tugberk
by Tugberk on Thursday, May 31 2012 07:07:39 +03:00

@Emre

Buradaki auth filitresinin SSL ile bir bağlantısı bulunmamakta. Başka bir değişle, ApiKeyAuthAttribute isteğin HTTPS üzerinden gelip gelmediği ile igilenmemekte.

Emre Turan
by Emre Turan on Sunday, Jun 03 2012 21:45:09 +03:00

Cevap için teşekkür ederim.

Bir kaç gündür takıldığım bir nokta var. 

Mobile applicationlar için Mvc4 ile bir web api yazıyorum. Gelen requestleri validate etmek için de senin dll i kullanma kararı aldım ama bu konuya senin kadar hakim olmadığım için şu sorunu çözemiyorum.

Örneğin ağ trafiğini izleyen her hangi bir kişi, application üzerinden gönderilen requestlerin querystringlerindeki (form içinde post dahi ediliyor olsa) apikey i çok rahatlıkla görebilir ve bu apikey ile benden istediği bilgiyi çekebilir ya da post edebilir.

Bu sorunun önüne nasıl geçilebilir sence?

Saygılar,

Emre Turan.

Tugberk
by Tugberk on Monday, Jun 04 2012 03:51:09 +03:00

@Emre

Kesinlikle SSL kullanmalısın. Aksi halde network sniffing yapan birisi tüm request'leri görecektir. SSL'i IIS level'da veya app level'da force edebilirisin. Örneğin, ASP.NET Web API'da SSL force eden bir action filter: https://github.com/tugberkugurlu/WebAPIDoodle/blob/master/src/apps/WebAPIDoodle/Filters/RequireHttpsAttribute.cs

Ayrıca uygulamanın yapısına bağlı böyle bir auth kullanmak. Seninde görebileceğin gibi oldukça zayıf bir sistem bu ama basit.

Api Auth + Claim-based auth kullanarak daha güvenli bir sistem yaratabilirsin.

http://zamd.net/2012/05/04/claim-based-security-for-asp-net-web-apis-using-dotnetopenauth/

Ayrıca bu yazı ASP.NET Web API beta sürümüne göre yazıldı. Ürünün RC sürümü çıktı birkaç gün önce. Nuget'e koyduğum bu paketide değiştirdim. Kaynak kodunu https://github.com/tugberkugurlu/WebAPIDoodle adresinden bulabilirsin. Nuget paketi ise: http://nuget.org/packages/WebApiDoodle

Emre Turan
by Emre Turan on Monday, Jun 04 2012 09:17:02 +03:00

Teşekkür ederim bu bilgiler çok iyi oldu.

Bu arada İstanbul' da çalışmayı düşünmüyor musun?

dsdsa
by dsdsa on Friday, Jun 29 2012 04:42:00 +03:00

I looks pretty good mate. But I installed :Install-Package TugberkUg.Web.Http -Pre, it wiped off all my existing dlls, and ruined my application. People who use this, be aware of it!!

SoyUnEmilio()
by SoyUnEmilio() on Wednesday, Oct 17 2012 00:11:44 +03:00

Reading your article comes to my mind that authentication && authorization could be implemented througouth Authorization filter attribute.

Reading other articles says that authorization should be separated from authentication (Dominick Baier).

I'am very confused about this two terms (authentication && authorization) after reading this post.

In my understanding:

- authentication allows you to identify a user in your system.

- authentication should be implemented at message handler level because it's at the very begining of the stack and you can prevent a user from reach next level in the stack if it have not been authenticated by the system. For example you could stop a user from getting inner in the stack if he has not sent an authorization header.

- authorization allows you to grant access to any authenticated user througout roles, functions, etc.

- authorization should be implemented at filter attribute level because you are trying to reach the resource and you need to confirm if the user has the correct grants.

Mixing both of them causes a lot of confussion to me.

Which is the best approach ?

Tugberk
by Tugberk on Wednesday, Oct 17 2012 07:50:04 +03:00

@SoyUnEmilio

Very good point and I have been planing to write another blog post just about that for like a month or so (and this just confirms that I have to do it very quickly). I think that Dominick is absolutely right on this one. I wrote this article at the early stages of the framework and I mixed the concepts a lot. So, this is not a good way of implementing authantication and authorization.

IMO, you should implement your authantication through a message handler and you almost always don't want to try to perform authorization inside that handler. IMO, you may only return Unauthorized inside the message handler if the user is not authanticated at all. Here is a message handler example for API Key authentication: ApiKeyAuthenticationHandler.cs.

The authorization part can be handled by the System.Web.Http.AuthorizeAttribute. The AuthorizeAttribute will check against the Thread.CurrentPrincipal. So, the principal that you have supplided inside your message handler will be checked against.

Thanks for bringing this up and I hope this helps.

David
by David on Monday, Nov 05 2012 16:09:10 +02:00

The above referenced example is for Framework 4.5, right?  Is there an example of using APIKey Authentication with a Web API application on framework 4.0?

Romy
by Romy on Wednesday, May 15 2013 16:27:51 +03:00

Hello Sir,

I am new in Web api and i am using your sample code.There is no any errors in my web api but when i run with same url as shown in your last image, it display nothing and when i remove  [ApiKeyAuth("apiKey", typeof(InMemoryApiKeyAuthorizer), Roles = "Admin")] from the controller class it display the result . And also how i call method with parameters. Please send me the code on my e-mail id :testing.client139@gmail.com

Many Many Thanks,

Romy

 

fdfd
by fdfd on Thursday, Feb 26 2015 05:28:21 +00:00
fdfdf
Escorts in Dehradun
by Escorts in Dehradun on Tuesday, Sep 06 2016 06:56:27 +00:00
Your Site Is Very Good and The Post is Well On Topic, Thanks for Sharing it with us.
Anu
by Anu on Tuesday, Sep 06 2016 06:59:05 +00:00
Awesome article, Thanks for share with us.

Tags