Overview

Pages 13

Clone this wiki locally

The What

Warden is a Rack-based middleware, designed to provide a mechanism for authentication in Ruby web applications. It is a common mechanism that fits into the Rack Machinery to offer powerful options for authentication.

Warden is designed to be lazy. That is, if you don’t use it, it doesn’t do anything, but when you do use it, it will spring into action and provide an underlying mechanism to allow authentication in any Rack-based application.

The Why

With the push towards using Rack applications many opportunities are opening up. The promise of multiple applications running in the same process, sub-applications and sub-sub-applications can be realized.

The lure of multiple applications is appealing to many. One of the questions however is how to manage authentication in this situation. Each application could require authentication or a “user”. Overall, authentication will likely be the same “user” for all applications in the Rack graph that is allowed to log in to the system. Authorization aside.

Warden allows all downstream middlewares and endpoints to share a common authentication mechanism, whilst still allowing the overall application to manage it. Each application can access the authenticated user, or request authentication in the same way, using the same logic throughout the Rack graph. Each application can layer whatever sugary API on top, and the underlying system will still work.

The How

Warden sits in the Rack stack, after the session middleware (that stores a session, hash-like object in env['rack.session']).

Warden injects a lazy object into the Rack environment at env['warden']. This lazy object allows you to interact with it to ask if it’s authenticated or to force authentication to occur in any downstream piece of Rack machinery. If the request is authenticated, warden gets out of the way. If it’s not, you can “fail” it and cause it to react.

env['warden'].authenticated? # Ask the question if a request has been previously authenticated
env['warden'].authenticated?(:foo) # Ask the question if a request is authenticated for the :foo scope
env['warden'].authenticate(:password) # Try to authenticate via the :password strategy. If it fails proceed anyway.
env['warden'].authenticate!(:password) # Ensure authentication via the password strategy. If it fails, bail.

If you don’t want to authenticate a request, just don’t ask env['warden'] to authenticate it.

After authentication is performed, if successful, it will provide access to the “user” object. This can be anything except nil.

By placing it directly after the session middleware, all downstream middleware and applications will have access to the authentication object. This allows all applications to have a combined, coherent approach to authentication even if they’re dropped in from another source. All Rack middlewares and endpoints can use the same underlying authentication system, even if they have layered different sugary APIs on top.

A “strategy” is the place where the logic of authentication is actually run. See Strategies for more information.

Failing Authentication

When authentication should be failed, simply throw a :warden symbol at any point downstream of the Warden middleware. You can also add an options hash to the throw to include arbitrary information about the throw.

throw(:warden) # Bail out for an authentication failure
throw(:warden, :stuff => "foo") # Bail out with some options to provide some context

What this does is bail out to a “failure application” that you must set up. The failure application, a standard Rack application, is there to handle cases of failed authentication. You may use it to render a login form for example.