Shared Auth for Rack Apps  September 26th, 2011

Being able to mount rack apps inside each other is awesome. For example, Resque provides Resque::Server for monitoring job processing and that means I wont have to write an interface for it myself.

Great, /admin/resque is going to be what I want with practically no work on my part. But since I'm using these for real in the admin area of Bugrocket – there's a problem with just mounting apps under /admin. The parts that I wrote myself use my authentication layer — and they make sure you're actually an administrator!

I could wrap Resque::Server in some other rack app I write, which would duplicate the logic and require the user to login again. Or maybe use HTTP basic auth... not much better.

Instead I've pieced together some ideas I found online (like this slideshare presentation) and have a method of writing authentication once with Warden and sharing it easily with other rack apps.

Note

This is written from the perspective of mounting micro-apps like Resque::Server and Route53::Web inside a Rails 3.X app. Warden is pretty flexible though, so you could conceivably do this with any rack app as the parent.

Setup

Configuration

config/initializers/warden.rb is where we'll make this all happen.

First we tell Warden the strategies we plan to use. :admin is an arbitary name – it's what I'm going to be authenticating. Then you can specify whichever controller you want the user to be redirected to when they need to login. In my case I have a sessions controller in the Admin namespace, pretty typical.

Next you need to define some hooks Warden will use to get and persist the current user to the session:

Again pretty typical.

Authentication Strategy

Now we get into the meat. We need to define what the :admin strategy we mentioned earlier actually does to authenticate a user.

The authenticate! method is the logic that used to be part of a helper in my actual admin controllers. We're moving it here so it can be shared.

Wiring It Up

So at this point we can already go ahead and use this code for authenticating in the parent app, replacing all the auth code we used to have written directly with the new Warden-backed implementation.

You probably have a helper somewhere, something like this:

Well now we can just change it to:

Similarly, we can just call the same warden.authenticate! :admin in our Admin::SessionsController#create method and no longer need to write the authentication logic in our controller – everything is handled by Warden.

Mounted Apps

So assuming all of that is working you have successfully replaced your inline authentication, but nothing has really changed. Users still wont be asked to login when visiting mounted apps.

But this last step is super easy. We just define an authentication middleware:

This looks a bit fiddly but there's nothing to it. Basically we just transplant our Rails app's session configuration into rack.session.options, because that's where rack apps are expecting to see session info. Warden conveniently places a lazy-loaded instance into env for us, we call it just like we are in our regular admin controllers, and we receive many fresh sandwiches.

The last bit is just telling our mounted app(s) to use the new middleware.

Demo

Take a look at an example app that demonstrates all of this stuff.

Some social stuff: