On a recent project, I undertook the task of implementing a RESTful API using the new Asp.Net WebAPI framework. The aim was to support clients of all types, including a .Net desktop app and iOS and Android mobile apps. My API had to support some sort of authentication mechanism.
Since this was a basic application (to be used as a learning tool for the other developers on our team) we decided to use Basic HTTP Authentication. As the name suggests, it’s a simple protocol whereby the client sends an authorization token as a header in the HTTP request, and the server decodes that token to decide whether or not it is valid. If it is, the request continues, otherwise it (should) return a 401 Unauthorized response.
So how can we implement this with WebAPI? With an Action Filter, of course.
The Basic Authentication Action Filter
Start by creating a new class for your filter. This must inherit from System.Web.Http.Filters.ActionFilterAttribute, which is different from the normal namespace that are used for Asp.Net MVC Action Filters. This one lives in System.Net.Http.Filters. Be careful to subclass the correct type and don’t get confused. Also, override the OnActionExecuting method:
public class BasicAuthenticationAttribute : System.Web.Http.Filters.ActionFilterAttribute { public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) { } }
We will use this method to intercept the API and check that everything is OK with the security side of things (well, as OK as Basic Auth can be!). First, lets check we have an authorization header:
if (actionContext.Request.Headers.Authorization == null) { actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized); }
Simple stuff here. The fact that this action filter is executing implies that we want to protect the action that it attributes, and so if we don’t have a header, we’re not authorized.
If we have a header, lets parse the value:
else { string authToken = actionContext.Request.Headers.Authorization.Parameter; string decodedToken = Encoding.UTF8.GetString(Convert.FromBase64String(authToken)); string username = decodedToken.Substring(0, decodedToken.IndexOf(":")); string password = decodedToken.Substring(decodedToken.IndexOf(":") + 1); ... }
Just grab the header value, decode it from Base64 back to a string, and then split it. The value that is encoded would normally:, but really if this is a custom solution you can make it anything you want if you’re in control of how the value is encoded and decoded (which here I am – I just decided to follow the standard).
Now that we have the username and password, it really is up to you as to how you use it. The normal thing would be to look up some database value and check if the user exists. In my case, I grab a couple of services to find the user that the credentials refer to, and I set the user into the current principal. I then defer execution to the base filter and allow the action to run:
IPasswordTransform transform = DependencyResolver.Current.GetService<IPasswordTransform>(); IRepository<User> userRepository = DependencyResolver.Current.GetService<IRepository<User>>(); User user = userRepository.All(u => u.Username == username && u.PasswordHash == transform.Transform(password)).SingleOrDefault(); if (user != null) { HttpContext.Current.User = new GenericPrincipal(new ApiIdentity(user), new string[] { }); base.OnActionExecuting(actionContext); }
If the user wasn’t found, simply return a 401:
else { actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized); }
In the code above where I set HttpContext.Current.User, I am just using a custom type called ApiIdentity, which is an implementation of IIdentity and allows me to store a user entity against the username. For brevity, it’s implementation is:
using System.Security.Principal; public class ApiIdentity : IIdentity { public User User { get; private set; } public ApiIdentity(User user) { if (user == null) throw new ArgumentNullException("user"); this.User = user; } public string Name { get { return this.User.Username; } } public string AuthenticationType { get { return "Basic"; } } public bool IsAuthenticated { get { return true; } } }
Using the Basic Authentication action filter
To use this thing, just decorate any action or controller with [BasicAuthentication], and any requests to those actions will require that the Authorization header is sent:
// GET /api/accounts [BasicAuthentication] public IEnumerable<OwnerAccountDto> Get() { var accounts = _accountsRepository.All(a => a.OwnerKey == AuthorizedUser.Guid).ToList(); return Mapper.Map<IEnumerable<OwnerAccountDto>>(accounts); }
I went one step further than this and created an AuthorizedApiController which already has this attribute on it. Furthermore, I added an accessor to get the actual user entity that was authorized when the request was made:
[BasicAuthentication] public class AuthorizedApiController : ApiController { public User AuthorizedUser { get { return ((ApiIdentity)HttpContext.Current.User.Identity).User; } } }
A quick note on unit testing: having this property return something straight out of the current HttpContext instance mucks things up a little for unit testing, since there won’t be a context to look at in that scenario. The way to combat it is to either:
- Create a provider model to retrieve the authenticated user through some IAuthenticatedUserProvider interface
- Use a framework such as Moq to allow you to mock up a context just for this scenario (available through Nuget too)
Now you have the tools to custom-build an authentication scheme for your Web Api. Happy.. authenticating!