A question I get over and over again when presenting on the ASP.NET Web API is, “How does Authentication and Authorization work in the Web API?” This seemingly simple question is actually one of the most complex aspects of the Web API; largely because the framework ignores any formal support for it. Sure, we have the AuthorizeAttribute, but as we’ll see, this has many limitations that prevent it from being a viable option. We can rely on a hosting environment such as IIS, but this also has its limitations. At the end of the day, to address security, we as service developers need to get our hands dirty and do some actual work. The trouble is that, with a plethora of approaches out there, navigating the topic gets overwhelming very quickly. As such, my modus operandi for this blog series is to present and demystify some of the common options available. I will not cover all of them (that would take a book or twelve), but I will cover the most common solutions to the problem. The intention is to give the .NET community what I wished I had when I started working with the ASP.NET Web API.
So for all of the folks out there who have asked me about the Web API’s security model and been given sub-par explanations, I hope this blog series can help clear up some of the confusion for all of us!
What the Series Will Cover
From a bird’s eye view, the list below outlines the concepts this series will cover. The truth is, one could write a blog series on these concepts individually; but, I will attempt to distill the information in a way that is practical and still covers the essential aspects of each topic.
- AuthorizeAttribute
- Basic Authentication
- Digest Authentication
- Security Token Services
- OAuth and OpenID
Security Primer
When looking at the topic of application security, there are many factors involved. Depending on your situation, your idea of secure may be different from someone else’s. For example, the security demands on a banking system will likely be much more stringent than that on a reservation system for a hair salon. That being said, I do want to review the core concepts involved in system security. If you find yourself hungry for more; a much more in-depth summary can be found here.
- Confidentiality – Ensuring that only those intended to have access to your data can gain access to it.
- Integrity – Ensuring accuracy of your data
- Authenticity – Ensure that both parties involved in a transaction are who they say they are
There are several other concepts involved such as Non-repudiation and Availability. However, I am going to focus on what I consider the core concepts and what is most often needed when building a system.
Part 1 – Moving Towards Basic Authentication
The code examples can be found here. Do what you will with it, but as always, no warranty for you!
The ASP.NET Web API does very little address to the security story. For better or worse it is not addressed. Much of the documentation and examples out there show the usage of the AuthorizeAttribute, or the heavy reliance on IIS to manage Authentication and Authorization. In Part 1 of this series, we will explore why the AuthorizeAttribute falls short, and how we can make some minor enhancements to our API to move to the HTTP Basic Authentication Standard. So, with introductions out of the way, let’s get started!
Your Friend/Foe – the AuthorizeAttribute
Oh… if only the security story was as easy as adding an attribute to your controller actions, what a great world it would be. It would also be nice if we did not need to worry about security. Unfortunately, both of these niceties are a pipe dream. For now let’s play the devil’s advocate and take a quick look at how the AuthorizeAttribute works. Then we can talk about why you should never use with a production grade service.
Using the AuthorizeAttribute is very simple. As with its MVC counterpart all you need to do is decorate your action method or controller class with it and BAAAM! Now you have security. Let’s look at a quick example.
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; namespace WebAPIAuth.Controllers { public class TestController : ApiController { [Authorize] public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } } }
By simply adding this attribute to your action method, any unauthenticated requests to our Web API will reply with a HTTP 401 (unauthorized) response. Take a look at the request below to the TestController.
Pretty slick right? Well, not so fast. Though this is simple enough to implement, there are several serious limitations to doing things this way. The central problem with this approach is the underlying mechanism it uses to do its work. Both the ASP.NET Web API’s AuthorizeAttribute and its MVC counterpart rely on Forms Based Authentication to authenticate incoming requests. In Forms Based Authentication, cookies are used to transport the authentication ticket between client and server. Not only does the use of cookies add state to our services that should be stateless, they also limit the potential set of clients to only those that understand how to work with them. This limitation is unacceptable in today’s landscape with such a disparate set of potential clients. From an architectural stand point, there are additional concerns to consider. Chief among them that the AuthorizeAttribute deals only with Authorization. For Authentication, it relies on the ASP.NET Pipelines FormsAuthenticationModule. Therefore, unless you are using it in conjunction with an ASP.NET application you really cannot rely on this approach at all. From a security standpoint let’s look at how this approach holds up to our security concepts.
Does it support Confidentiality?
No. Any servers between a request and your Web API would be able to see the content of the request and response.
Does it support integrity?
No. It does nothing to ensure that data has not been tampered with by the time a request reaches your Web API.
Does it support Authenticity?
At some level this is supported. The authentication ticket is passed from client to the server via cookies. However, these kinds of ad-hoc implementations are inherently susceptible to phishing and unless some sort of transport security is used in conjunction with cookies, credentials are sent across the wire in clear text.
To sum it up, because the AuthorizeAttribute relies on Forms Based Authentication, we cannot and should not ever use it to secure our Web API.
Basic Authentication
So now that we know using Forms Based Authentication is a non-starter, the next thing we can consider is Basic Authentication. Basic Authentication is a very attractive option since it is part of the HTTP standard. Moreover, as the name states it is a very simple form of authentication. Let’s quickly outline the steps involved in Basic Authentication.
- Client makes a HTTP Request to a protected resource
- Server responds with the HTTP 401 (unauthorized) status code. The response should also contain the ‘WWW-Authenticate’ header
- The presence of the ‘WWW-Authenticate’ header indicates to the client that authentication is required
- Client makes the same request passing along the credentials in the ‘authorization’ header
- Server authenticates the user using the supplied credentials
- Server authorizes that the user has access to the request resource
- If the user is authorized the resource is returned to the client, if not an HTTP 401 (unauthorized) is returned
Here is a simple sequence diagram outlining Basic Authentication from one of my favorite tools.
This simple approach does not require cookies, session, or any other type of state to be managed by our Web API. However, there are some concerns worth noting here. First, because our Web API is supposed to be stateless, Basic Authentication requires the client to send its credentials with every request. This forces the server to authenticate and authorize each request, potentially introducing some unwanted overhead depending on how your application does these things. Secondly, Basic Authentication does nothing to protect the credentials being sent across the wire. Looking at the example of the authorization header below; you may be led to believe that there is some sort of encryption going on, I assure you there is not.
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
The value of the Authorization header may look encrypted, but it is actually the name and password encoded in Base64. This means the clients credentials are essentially sent to the server in clear text. For Basic Authentication to be viable at all, we must always use it in conjunction with some form of TLS (think SSL all the time). Let’s take a look at how we might implement Basic Authentication with the ASP.Net Web API.
Basic Authentication Implementation
The first instinct one may have for an implementation might be to roll your own AuthorizeAttribute. This attribute can then be applied to any actions that you want to protect. But from a Separations of Concerns (SoC) perspective, Authentication and Authorization are completely different animals. Authentication focuses on identifying the user, Authorization focuses on what the user can do in a given system. Having an action filter responsible for both concepts goes against most core object-oriented principles. Even more troubling is how close to your action method the Authentication and Authorization would happen. If we wait until the request makes its way to an Action Filter before even Authenticating the user, a malicious request could make its way much deeper into our call stack than is comfortable. Think of it this way. Is it better lock only your bedroom door while you are sleeping, allowing burglars to make their way into your house, up the stairs, only to be stopped at your bedroom door? Of course not! A more reasonable approach would be to lock all the doors of your house, so burglars cannot intrude in the first place! Similarly, we should reject all unauthenticated requests as soon as possible, limiting the potential damage a malicious request could cause. This is where the Web API’s Message Handlers come in handy. Message Handlers are executed very early in the life cycle of the request/response pair. Message Handlers receive requests when they come in as well as the responses before they are sent back. This allows Message Handlers to inspect and reject a request before it makes its way to our controllers. Message handlers are chained together, much like a linked list, where one handler passes the request to the next. Once the last handler is executed the response is processed much the same way but in reverse. The image below illustrates the ‘pipeline’ style architecture of Message Handlers.
By creating a custom message handler to manage Authentication we can inspect the request as well as alter the response, but better yet, we can do it at a very early stage of the request/response life-cycle. Let’s take a look at how we might implement this.
The Message Handler
using System; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using System.Web; namespace WebAPIAuth { public class AuthenticationMessageHandler : DelegatingHandler { protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request , System.Threading.CancellationToken cancellationToken) { if (request.Headers.Authorization != null && request.Headers.Authorization.Scheme.Equals("basic", StringComparison.OrdinalIgnoreCase)) { var credentials = request.Headers.Authorization.Parameter.Split(':'); //Look up user var user = new UnitOfWork().Users.GetByCredentials(credentials[0], credentials[1]); if (user != null) { HttpContext.Current.User = user.ToClaimsPrincipal(); Thread.CurrentPrincipal = HttpContext.Current.User; } } var httpResponse = await base.SendAsync(request, cancellationToken); if (httpResponse.StatusCode.Equals(HttpStatusCode.Unauthorized)) { httpResponse.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("basic")); } return httpResponse; } } }
This simple message handler does everything needed to support Basic Authentication.
- It checks the request for the presence of the Authorization header.
- If found it parses the header and extracts the username and password.
- It then looks up the User based on those credentials, converts the User to a Claims Principal and finally assign it to HttpContext.Current.User and the Thread.CurrentPrincipal.
- From there it passes execution to the next handler.
- Once the response comes back it checks the response HttpStatusCode for a 401 (unauthorized) status. If the response is a 401 we add the ‘WWWAuthenticate’ header to indicate to the client that they need to supply their credentials.
One last thing worth mentioning is the use of the ToClaimsPrincipal extension method. This extension method converts our User object into a ClaimsPrincipal object. Click here for more information about Claims Based Authentication. The code for this is very straight forward. The only thing we are doing here is creating a ClaimsPrincipal object and adding a few claims to its claims collection.
using System.Collections.Generic; using System.Globalization; using System.Security.Claims; using System.Security.Principal; using WebAPIAuth.Models; namespace WebAPIAuth { public static class UserExtensions { public static IPrincipal ToClaimsPrincipal(this User user) { var claims = new List<Claim> { new Claim("UserName", user.UserName), new Claim("UserId", user.Id.ToString(CultureInfo.InvariantCulture)), new Claim(ClaimTypes.Role, user.Role), }; var claimsIdentity = new ClaimsIdentity(claims, "Basic", "UserName", ClaimTypes.Role); return new ClaimsPrincipal(claimsIdentity); } } }
Now that our message handler is ready, we need to make sure it is executed for each request. To do this, you need to add the following configuration to your WebApiConfig.cs.
using System; using System.Collections.Generic; using System.Linq; using System.Web.Http; namespace WebAPIAuth { public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); config.MessageHandlers.Add(new AuthenticationMessageHandler()); } } }
With the message handler intact, it will now handle Authentication for us. However, that is not the entire story; we still have the matter of Authorization. Thankfully for that problem we can still rely on the AuthorizeAttribute as it is. When a request reaches our action method, the AuthorizeAttribute will look for an authenticated principal on the current thread. If the principal is present and authenticated, the next step is user authorization. If the current principal is authorized to access the requested action, the action will be executed, and the result return to the client. On the other hand, if there is no authenticated user present, or the user does not have access to the requested action a HTTP Status 401 (unauthorized) will be returned.
In Action
With all of the moving parts ready to go, let’s take a look at this approach in action.
The First Request
The first thing we will do is make a HTTP Request for a protected resource. We will not include our credentials in the authorization header to get a HTTP Response with a status code of 401 (unauthorized).
Looking at the highlighted lines above you can see the request to the protected resource and the 401 (unauthorized) response that was returned. You also can see that the ‘WWW-Authenticate’ header was included. This tells the client that credentials are required.
The Second Request
When clients receive a 401 (unauthorized) response, they can send an additional request, one that contains the authorization header and the credentials of the user making the request. Let’s look at an example of this.
With the presence of the Authorization header complete with the user’s credentials, the user can be authenticated, and all is well with the world.
Wrapping Up
In part one of this series, we looked at a few different things. We looked at the limitations of the AuthorizeAttribute and its reliance on Forms Based Authentication. We looked at how we can support Basic Authentication in our Web API’s. Although Basic Authentication is a very attractive solution because it is both simple, and part of the HTTP standard, it does have some limitations. First and foremost because it offers no type of transport layer security using it without SSL is a not an option. If you are going to use basic authentication, make sure you do it in concert with SSL. Another area of concern is that the credentials are sent to the server with each request, which creates a potential for additional processing overhead. Finally, if your browser caches these credentials, there is some risk of Cross Site Forgery attacks. Even with these limitations, Basic Authentication is the first step to a much more secure Web API. We will take this implementation much further in Part 2 of this series, where we will take a look at Digest Authentication. Digest Authentication is very similar to Basic Authentication, but it is substantially more secure. The additional security precautions of Digest Authentication address most of the concerns that arise from Basic Authentication.
Until next time keep your services safe!
BDN
Hi! I just wanted to say that this blog post was some of the best material I’ve come across so far regarding security for the .net web api Really hope that the next part of the series will show up soon.
Niklas,
Thanks for the kind words. I am hoping to add part 2 soon. I have already started working on it.
BDN
Hi
If you can provide the links for the rest of the series, that would be great!
Thanks,
Sadly I have not had enough time to get the others finished as of yet. If you are looking for more material,there is a really amazing book on the topic(I am not affiliated with this book in any way. But it is the best book on the topic of security I have read).
http://www.amazon.com/Pro-ASP-NET-Web-API-Security/dp/1430257822
I understand… Thanks for sharing the book.
Great guide,
when will be the second part published? Waiting for the ‘Digest Authentication’.
Thanks a lot for your effort so far. 🙂
Sadly I have not had enough time to get the others finished as of yet. If you are looking for more material,there is a really amazing book on the topic(I am not affiliated with this book in any way. But it is the best book on the topic of security I have read).
http://www.amazon.com/Pro-ASP-NET-Web-API-Security/dp/1430257822
I do plan on completing the rest of the series. I just have not had much time this year do blog.
It’s hard to find educated people on this topic, but you sound like you
know what you’re talking about! Thanks
Why, in your opening, do you say the question is a “seemingly simple question”? There’s absolutely nothing “seeming simple” about it to me.
Only because it seems like something you would get for free with the framework.