This post was published more than 8 years ago. It's likely the contents are obsolete or nostolgic. If you think the contents are worth updating, open an issue. Otherwise, sit back and enjoy the stroll down memory lane.
One of the biggest challenges in web development is knowing when to break your monolithic application into services. If you segment too soon, you risk introducing unnecessary complexity. On the other hand, if you wait too long, your application is prone to becoming an unmaintainable ball of mud. I obviously can’t give you a concrete answer to this problem. However, if you decide to break your application into services, there is one thing that remains constant; securing your services’ endpoints. I’ve found Rack middleware to be an excellent tool for the job.
I had read quite a bit about Rack Middleware. I had seen a talk, watched a Railscast, and often used other peoples’ middlewares in my day-to-day Rails development. However, if you’re anything like me, sometimes things don’t fully sink in until you have an opportunity to get your hands dirty. Today we’re going to do just that!
That being said, although the majority of this code was extracted from a real project, the following code and accompanying repository exists for the sole purpose of demonstrating how easy it is to get started using Rack middleware to secure your APIs endpoints.
Since I’m not a huge fan of long boring blog posts, let’s make it
nerdy awesome by sprinkling some Star Wars on it. Feel free to follow along with the source on Github.
A long time ago, in a galaxy far, far away…
After the destruction of the first Death Star, the Imperial IT department has decided to outsource some development. Apparently one of their developers forgot to properly secure their API, allowing a small group of Rebel hackers to steal the plans for the Death Star. After some “management changes”, it is now up to us to prevent the Rebels from exploiting the Imperial’s service oriented architecture.
But, little do the Sith know, we were using a Jedi mind trick!
To successfully pull off this ruse, we’ll be ensuring that only clients who sign their requests according to OAuth 1.0 RFC 5849 Section 3, a protocol unknown to even the most chatty protocol droids, are allowed access. However, instead of polluting the Imperial API with logic concerning validation of requests, we’ll be wrapping it all in Rack Middleware, and distributing it as a gem!
When talking about Rack, it’s a given that someone will mention Rack’s simple API. It really is a thing of beauty. There’s only a couple things you need to know before you can start hacking:
- You need to define an initialize method that accepts at least one argument, the Rack application. In other words, Rack Apps are to Rack middleware, as Jedi are to the Force.
- Your middleware needs to respond to
call, accepting an env (request environment). Much like a Jedi senses disturbances in the Force, a Rack middleware uses the
envto react to, um… disturbances in the request.
Now for some setup! Woo! Excitement!
Edit your Gemfile as follows:
Edit your gemspec to include the following dependencies:
“bundle” it up and we’re ready to start rockin’. (Please see source for Rakefile and spec_helper boilerplate)
Whoa there turbo! Before we get started, let’s make a few assumptions. The Imperial’s API already implemented an ImperialClient model that stores all the registered clients’ consumer keys and secrets. Also, we’re not checking OAuth nonces, leaving the Imperials open to replay attacks.
Little do they know, we’ve already emailed a set of Imperial credentials to the Rebels. Now we just have to make sure to help them implement the protocol by sending them helpful error messages in the response body.
So, let’s start by failing some tests (spec/lib/forcefield/middleware_spec.rb):
If you’re unfamiliar with Rack, you may find the
let(:death_star) line a little peculiar. Well, it’s actually creating a mock Rack Application. This is because calling a Rack application results merely in an array containing a status code, HTTP response headers, and an array of strings that serve as the response body.
In combination with
Rack::MockRequest, I’ve found this pattern to be incredibly useful when testing Rack Middleware.
With that said, we can now watch our tests fail, and begin to implement the Forcefield class.
As stated above, we need to make sure we do the following:
initialize, taking a Rack application as an argument (see
- Responds to call, passing in the request environment.
That should be sufficient to make our tests pass, however, simply having an Authorization header isn’t good enough. It has to be a valid OAuth header. When implementing a protocol, as you often are when working with Rack Middleware, it’s nice to write specs against the RFC documentation. Here is an OAuth header adapted for our needs.
Let’s spec it out!
We’re going to need to mock out the ImperialClient model. I like to create mock classes for things like this in “spec/support”.
Simple OAuth - It’s simple!
In order to get these tests to pass, we need to seriously parse the Authorization header. Certainly there exist some tools that can make this much easier for us. After fooling around with various OAuth libraries (looking at you oauth-ruby), I came across a pleasant abstraction - simple_oauth.
Also, this seems like an excellent place to start thinking about encapsulation and separation of concerns. We should probably have an object that handles verifying the request. Luckily, we need not look any further than Rack itself. We’ll just subclass
Rack::Auth::AbstractRequest, as it already provides a handful of useful helper methods. As I learned, be sure to give the Rack docs a once over before you end up reinventing the wheel. The Rack library provides a lot of potentially reusable, battle-tested code.
The astute reader may have noticed that I’m calling some methods that appear to have come from nowhere. As stated above, this is because we are inheriting methods from Rack::Auth::AbstractRequest. Let’s take a second to document what those methods are doing:
- #provided? - Checks to see that the request env has included an Authorization header. Similar to what we had been doing prior to our refactor.
- #scheme - Parses the authorization scheme from the header.
- #params - We’ve actually decided to overwrite this method to better fit our needs. We’ll use it to strip apart the OAuth params from the Authorization header via
Refactoring our existing code
In my opinion, this refactor is a much better separation of concern. Our
Forcefield::Middleware class is now only responsible for one conditional to determine the validity of the request, delegating all the logic for handling incorrectly signed requests to our
We now have separated our request parsing code from our middleware’s core logic. This leaves us with a lean, readable entry point for our middleware. Some additional tests, oauth specific edgecases and niceties can be found in the source on Github
Now that we’ve snuck our little gem into the Deathstar’s API, the Rebels stand a chance!
Oh, and why not - May the force be with you!
See a mistake? Kindly let me know by filing an issue