Ruby On Rails

Rails API Authentication With Warden (Without Devise)

Background

When it comes to Rails authentication, devise is the go-to gem: “Devise is a flexible authentication solution for Rails based on Warden”

But sometimes, all the flexibility and convenience of Devise might not be necessary.

Recently, I had to add some custom authentication to a small rails-api application.
It was a really lightweight, didn’t have a database and the authentication was done against an external service.
Devise wasn’t going to work out of the box so I decided to dive one layer deeper and work directly with Warden.

Warden performs authentication at the rack middleware level. It does this by using Strategies.
Warden only provides a Base strategy from which other must inherit.

Devise features

Before getting rid of Devise, let’s see what it adds to warden:

  • a few Strategies which you are probably familiar with (DatabaseAuthenticatable, Rememberable)
  • Controller filters and helpers authenticate, user_signed_in?, current_user, user_session, …
  • All controllers and views. Which we don’t really need for an API.
  • A default failure application responsible for redirection

Doesn’t sound too bad, doesn’t it? Let’s get started!

Implementation

Sample Application

We’ll start with a really simple rails-api application that display the current time:

$ rails g controller Welcome index

Let’s also add the warden and bcrypt (to use has_secure_password) gems to our application.

# Gemfile
gem 'warden'
gem 'bcrypt'

Note: the code source is available at https://github.com/ouranos/rails-api-warden

User Model

In my case, the app didn’t have a database and was authenticating against an external service.
For this example, we’ll keep it simple and use a User model.

Our model will have the following fields:

  • an authentication token that can be passed as a parameter
  • an encrypted password, that can be used to authenticate via HTTP Basic auth

Add Warden to the Rack Middleware stack

As explained in the setup page, we need to add Warden to our application middleware stack.

Let’s have a look at our current middleware stack:

$ rake middleware

use ActionDispatch::Static
use Rack::Lock
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x000000052979d0>
use Rack::Runtime
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::RemoteIp
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::ParamsParser
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
run RailsApiWarden::Application.routes

That’s a big list (and it’s even longer when using Rails)! Where should we put Warden?

According to the setup page, “Warden must be downstream of some kind of session middleware.”

Well, Rails API doesn’t enable any session management by default so let’s put it after the ActionDispatch::ParamsParser:

Let’s check that Warden is in the stack:

$ rake middleware

use ActionDispatch::Static
use Rack::Lock
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x000000037c80f0>
use Rack::Runtime
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::RemoteIp
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::ParamsParser
use Warden::Manager
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
run RailsApiWarden::Application.routes

Great!

Now the documentation says “It must have a failure application declared, and you should declare which strategies to use by default.”

We’ll worry about the failure application later, let’s first add a strategy.

Strategy – Authentication Token

We’ll define a strategy to authenticate users via an authentication_token passed in the params.

As explained in the Strategy page a Strategy is a class that inherits from Warden::Strategies::Base and implements an authenticate! method.

#authenticate! tries to find an user matching the authentication_token parameters and calls a method according to the result.

We’ve also added a #valid? method. #valid? is optional and acts as a guard method for the strategy. If it returns false, the strategy will not be executed. In our case, we skip the strategy if no authentication_token has been provided. This can be useful when combining multiple strategies as we’ll see later.

You can see that the authenticate! method calls fail! or success!. These are public helper methods provided by Warden. Here are the most commonly used ones:

  • #success! Whenever you want to provide a user object as “authenticated”. This will halt the strategy, and set the user in the appropriate scope. It is the “login” method.
  • #fail Causes the strategy to fail, but not halt. The strategies will cascade after this failure and warden will check the next strategy. The last strategy to fail will have it’s message displayed.
  • #fail! Causes the strategy to fail. Halts the strategies so that this is the last strategy checked

For more details you can read the Strategy page or if you’re feeling adventurous you can have a look at the Warden::Strategies::Base source where all these methods are defined.

Now that our strategy is implemented, we need to add it to Warden:

Controller methods

Let’s add the familiar Devise helper methods (current_user, signed_in?) to our controller as well as a before_filter to authenticate our users:

And it works!

warden_authentication_token

Adding a Failure Application

Now, you’ll realise that if you pass a wrong token you’ll get a pretty bad looking error page:

warden_no_failure_app

Remember that we didn’t declare a failure application? Let’s do this now!

Devise has a pretty intimidating one.

The failure application needs to be Rack application, so we can use ActionController::Metal to build a simple one and tell Warden about it:

Much nicer!

Much nicer!

Bonus round – Adding an extra strategy: HTTP Basic Authentication

Let’s a new strategy to our application. We want users to be able to authenticate via Basic Auth using their username/password combination.

Et voila:

warden_basic_auth

References

Some useful links:

Olivier Brisse