Thursday, 7 January 2016

Restival Part 5: Who Am I?

NOTE: Code for this article is at https://github.com/dylanbeattie/Restival/tree/v0.0.5

UPDATE: The WebAPI and OpenRasta implementations described here are... inelegant. After this release, I spent a little more time and came up with something much cleaner - which you can read all about in the follow-up post http://dylanbeattie.blogspot.co.uk/2015/12/restival-part-6-who-am-i-revisited.html

Welcome back. It's been a very busy summer that's turned into a very busy autumn - including a fantastic couple of weeks in Eastern Europe, speaking at BuildStuff in Lithuania and Ukraine, where I met a lot of very interesting people, discussed a lot of very interesting ideas, tried a lot of very interesting food, and generally got a nice hard kick out of my comfort zone. Which is good.

If your name's not on the list, you're not coming in!Anyway. As London starts getting frosty and festive, it's time to pick up where we left off earlier in the year with my ongoing Restival project - implementing the same API in four different .NET API frameworks (ServiceStack, NancyFX, OpenRasta and WebAPI).

In this instalment, we're going to add two very straightforward capabilities to our API:

  1. HTTP Basic authentication. Clients can include a username/password in an HTTP Authorization header, and the API will verify their credentials against a simple user store. (If you're doing this in production, you'd enforce HTTPS/TLS so that credentials can't be sniffed in transit, but since this is a demonstration project and I'm optimising for readability, TLS is out of scope for now.)
  2. A /whoami endpoint, where authenticated users can GET details of their own user record.

And that's it. Sounds simple, right? OK, first things first - let's thrash out our requirements into something a little more detailed:

  • GET /whoami with valid credentials should return the current users' details (id, name, username)

But remember - we're building an authentication system, so we should also be testing the negative responses:

  • GET /whoami with invalid credentials returns 401 Unauthorized
    • "invalid" means an unsupported scheme, an unsupported header format, invalid credential encoding, or a correctly formatted header containing an unrecognised username and/or password.
  • GET /whoami without any credentials returns a 401 Unauthorized
  • GET /whoami without any credentials returns a WWW-Authenticate header indicating that we support HTTP Basic authentication.

The WWW-Authenticate thing is partly a node to HATEOAS, and partly there to make it easier to test things from a normal web browser. I tend to use Postman for building and testing any serious HTTP services, but when it comes to quick'n'dirty API discovery and troubleshooting, I can't overstate the convenience of being able to paste something into a normal web browser and get a readable response.

The test code is in WhoAmITestBase.cs. Notice that in this implementation, our users are stored in a fake data store - FakeDataStore.cs - and we're actually using this class as a TestCaseSource in our tests so we maintain parity between the test data and the test coverage.

The WhoAmI endpoint was trivial - find the current user name, look them up in the user store, and convert their user data to a WhoAmIResponse. The fun part here was the authentication.

A Note on Authentication vs Authorization

NOTE: Authentication and authorization are, strictly speaking, different things. Authentication is "who are you?", authorization is "are you allowed to do this thing?" Someone trying to enter the United States with a Libyan passport is authenticated - the US border service know exactly who they are - but they're not authorized because Libyan citizens can't enter the US without a valid visa.

In one respect, HTTP gets this exactly right - a 401 Unauthorized means "we don't know who you are", a 403 Forbidden means "we know who you are, but you're not allowed to do this." In another respect, it gets this badly wrong, because the Authentication header in the HTTP specification is called Authorization.

Authentication - The Best Case Scenario

OK, to build our secure /whoami endpoint, we need a handful of extension points in our framework. We need to:

  1. Validate a supplied username and password against our own credential store
  2. Indicate that specific resources require authorization, so that unauthorized requests for those resources will be rejected
  3. Determine the identity of the authenticated user when responding to an authorized request

The rest - WWW-Authenticate negotiation, decoding base64-encoded Authorization headers, returning 401 Unauthorized responses - is completely generic, so in an ideal world our framework will do all this for us; all we need to do is implement the three extension points above.

Let's look at Nancy first, because alphabetical is as good an order as any.

NancyFX

Implementing HTTP Basic auth in NancyFX proved fairly straightforward. The first head-scratching moment I hit is that - unlike the other frameworks in this project - Nancy is so lightweight that I didn't actually have any kind of bootstrapper or initialization code anywhere, so it wasn't immediately obvious where to configure the additional pipeline steps. A bit of Googling and poking around the NancyFX source led me to the Nancy.Demo.Authentication.Basic project, which made things a lot clearer. From this point, implementation involved:

  1. Add an AuthenticationBootstrapper - which Just WorkedTM without any explicit registration. I'm guessing it's invoked by magic sufficiently advanced technology, because it overrides DefaultNancyBootstrapper.
  2. Implement IUserValidator to connect Nancy to my custom user store - my implementation is just a really simple wrapper around my user data store. My UserValidator depends on my IDataStore interface - and, thanks to Nancy's auto-configuration, I didn't have to explicitly register either of these as dependencies.
  3. In this bootstrapper, call pipelines.EnableBasicAuthentication() and pass in a basic authentication configuration.

protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) {
    base.ApplicationStartup(container, pipelines);
    var authConfig = new BasicAuthenticationConfiguration(container.Resolve<IUserValidator>(), "Restival.Nancy");
    pipelines.EnableBasicAuthentication(authConfig);
}

Finally, the WhoAmIModule needs a call to this.RequiresAuthentication(), and that's it. Clean, straightforward, no real showstoppers.

UPDATE: The NancyFX documentation has now been updated with a detailed walkthrough on enabling HTTP Basic authentication 

OpenRasta

Adding HTTP Basic auth to OpenRasta was a lot more involved - and the reasons why provide some interesting insight into the underlying development practises of the frameworks we're comparing. In 2013, there were some major changes to the request processing pipeline used by OpenRasta. As part of these changes, the basic authentication features of OpenRasta (dating back to 2008) were marked as deprecated - and until now, nobody's contributed an up-to-date implementation, I'm guessing because none of the people using OpenRasta have needed one. Which left me with a choice - do I use the deprecated approach, contribute my own implementation, or disqualify OpenRasta? Well, disqualification would be no fun, and deprecated code means you get Resharper yelling at you all over the place, so I ended up implementing a pipeline-based HTTP Basic authorization module to the OpenRasta codebase.

Whilst writing this article, I had a long and very enlightening chat with Sebastien Lambla, the creator of OpenRasta, about the current state of the project and specifically the authentication capabilities. It turns out the features marked as deprecated were intended to be migrated into a separate OpenRasta.Security module, thus decoupling authentication concerns from the core pipeline model - but this hasn't happened yet, and so the code is still in the OpenRasta core package. It's up to you, the implementer, whether to use the existing ('deprecated') HTTP authentication provider, or to roll your own based on the new pipeline approach.

The original pull request for this is #89, which is based on the Digest authentication example included in the OpenRasta core codebase - but shortly after it was accepted, I found a bug with the way both the Digest and my new Basic implementation handled access to non-secured resources. The fix for this is in #91, which at the time of writing is still being reviewed, so Restival's currently building against my fork of OpenRasta in order to get the authentication and WhoAmI tests passing properly.

Following those changes to the framework itself, the implementation was pretty easy.

  1. Provide an implementation of IAuthenticationProvider based on my IDataStore interface.
  2. Register the authentication provider and pipeline contributor in the Configuration class:

    ResourceSpace.Uses.CustomDependency<IDataStore, FakeDataStore>(DependencyLifetime.Singleton);
    ResourceSpace.Uses.CustomDependency<IAuthenticationProvider, AuthenticationProvider>(DependencyLifetime.Singleton);
    ResourceSpace.Uses.PipelineContributor<BasicAuthorizerContributor>();

  3. Decorate the WhoAmIHandler with [RequiresBasicAuthentication(realm)]
Total research and Google time took the best part of a day - including implementing the missing pieces of the framework. Implementation time once those were in place was around an hour, although I suspect even if the necessary authorization components already existed I'd have needed a couple of hours Google time to work out exactly how to plug into the pipeline.  

 

ServiceStack

Service was really straightforward, mainly because the extension points for overriding built-in authentication behaviour are logical and clearly documented. Implementation involved creating my own AuthProvider that extends the built-in BasicAuthProvider, injecting an IDataStore into this auth provider, and then registering my provider with ServiceStack in AppHost.Configure().

The only gotcha I encountered here was that the first implementation would response with an HTTP redirect to a login page if the request wasn't authorized - but Googling "servicestack authentication redirecting to /login" brought up this StackOverflow post, which explained that (a) this only happens for HTML content-type requests (my fault for using a web browser to test my HTTP API, I guess!), and that you can disable it by specifying HtmlRedirect = null when you initialize the auth feature:

public class AppHost : AppHostBase {
  public AppHost() : base("Restival", typeof(HelloService).Assembly) { }
  public override void Configure(Container container) {
    var db = new FakeDataStore();
    container.Register<IDataStore>(c => db).ReusedWithin(ReuseScope.Container);
    var auth = new AuthFeature(() => new AuthUserSession(), new IAuthProvider[] { new RestivalAuthProvider(db) }) {
      HtmlRedirect = null
    };
    Plugins.Add(auth);
  }
}

Total implementation time was about half an hour here - but bear in mind I've worked with ServiceStack a lot, so I'm familiar with the plugin architecture and the pipeline.

WebAPI

It took a surprisingly long time to come up with an HTTP Basic auth implementation on WebAPI. The first stumbling block here was the sheer number of posts, articles and examples demonstrating how to do it. There's a lot of excellent resources online about adding HTTP Basic authentication support to WebAPI, most of which are subtle variations on a common theme:

Now, the optimal number of references in a scenario like this is one. A single article, ideally written by the framework authors, saying "this is how you implement this very simple thing", and identifying the available integration points. Any framework where 3+ people have written in-depth articles on how to add something as simple as HTTP Basic authentication is insufficiently opinionated. Then there's the realization that WebAPI follows the ASP.NET MVC convention of decorating controllers and actions with attributes to support features like authentication - and injecting dependencies into attributes is fiddly, because of the way that attributes are instantiated by the runtime. So... fast-forward several hours of Googling and forking and compiling things and generally poking around, and I decided that Robert Muehsig's approach is the closest thing to what I'm after. And since it's on GitHub under an MIT license, I borrowed it. Most of the classes in Restival.Api.WebApi.Security are lifted directly from Robert's sample code. This produced a working implementation that passed all the tests, but I must admit I'm still not happy with it. It's definitely one I plan to revisit. It's notable that this implementation explicitly decouples the attribute itself from the filter implementation - this is so I can inject dependencies into the filter at runtime whilst still using the attribute declaratively, but it's rather inelegant and I can't say I'm terribly happy with it.

Total implementation time for this was at least a day, including research, reading examples, prototyping and digging into the WebAPI source to work out which bits to override. That's significantly more than I thought it would be, and I'm rather expecting someone to comment on this post "you're doing it wrong!" and link me to ANOTHER blog post or web page which demonstrates a different approach that "only takes five minutes". We shall see. :)

Conclusions

Here's how the four authentication implementations stack up. The implementation time here isn't how long it took, it's how long I think it would take to do it again now I'm familiar with the various frameworks authentication/plugin patterns.

Framework Lines of code New Classes Research time Implementation Time
WebAPI ~ 250 6 (here) 1 day 2-3 hours
ServiceStack ~ 20 1 (RestivalAuthProvider) 15 minutes 15 mins
NancyFX ~ 25 2 (UserIdentity, UserValidator) 30 minutes 15 mins
OpenRasta ~ 30

plus changes to framework
1 (AuthenticationProvider)

plus two new framework classes
1 day 1-2 hours

HTTP Basic auth proved an interesting example, because whilst it's an obvious feature on any API feature 'checklist', any reasonably mature HTTP API will almost certainly have moved beyond basic authentication to something more sophisticated such as per-client API keys or OAuth2 tokens. 

When it comes to ease of implementation, I'd say it's a dead heat between NancyFX and ServiceStack. I have more experience working with ServiceStack than I do with Nancy, and I suspect my familiarity with their plugin model made things a little easier, but I really don't think there's much in it - the integration points are sensible, the documentation is comprehensive (and accurate!), and I'm pretty confident the implementations presented here reflect the idioms of the corresponding platforms.

OpenRasta and WebAPI were much less straightforward, but for very different reasons, In a nutshell, I think WebAPI is insufficiently opinionated - rather than encouraging or enforcing a particular approach, it supports a wealth of different integration patterns and extension points, and it's really not clear why any one is better than another. OpenRasta, on the other hand, has a very strong authentication pattern that makes a lot of sense once you get your head around it - but a lack of examples and up-to-date documentation means it's quite hard to work out what the pattern is without digging into the framework source code and getting your hands dirty.

Tune in next time, when we're going to start adding hypermedia to our /whoami endpoint response.

Thanks to Jonathan Channon, Steven Robbins and Sebastien Lambla for their help and feedback on this instalment.

No comments: