How to use user authentication in your Padrino apps with padrino-warden

Nearly every web appliation requires user authentication. This introduction should help you save the evening I just spent trying to figure out how to use the Warden authentication layer in Padrino web applications.

What the Rack?

TL;DR -> If you want password-based authentication in your Padrino app – use this guide. 🙂

As you probably know Padrino (and many other Ruby frameworks) use Rack – which is the interface between the Ruby code and the actual web server that speaks HTTP. You can roughly compare it with CGI or WSGI (for Python). The advantage of Rack is that you can influence the way between the HTTP request, your application and the HTTP response. You can run the data through many different layers (called “middleware”) to mess with request and response. Warden is a Rack-based middleware that does exactly that. And it is designed to provide a mechanism for authentication in Ruby web applications. Warden is rather basic and acts transparently unless it is told to interfere. It is controlled through a Rack environment variable env[‘warden’].

Then sinatra-warden was conceived to make integration of Warden into Sinatra-based applications easier. Well, the software hasn’t been maintained for two years and its bug reports as just as old. But it seemed to serve as an example for the maker of padrino-warden. And since Padrino is based on Sinatra it helped make Warden easier to integrate into Padrino applications. So basically you just need t use the padrino-warden gem, configure how a successful login would work (depending on your database model or application logic) and create a template for a login page.

This may be trivia for experienced Ruby web developers. But it took me a while to figure out how things work together.

Installation

Add to the Gemfile of your project:

gem 'padrino-warden', :github => 'jondot/padrino-warden'

Why not just use the ‘padrino-warden’ gem that could get loaded from RubyGems? Well, the RubyGems version is 0.1.0 from 2010 – at least at the time I wrote this article. I already asked the maintainer to update the gem because his version on GitHub is actively maintained and currently (January 2014) version 0.19.0. I usually discourage using gems from GitHub because they are moving targets and next time you deploy your application you will probably get different code and different results.

Now in your shell from your project directory run…

bundle install

…and after a few seconds padrino-warden – and as a dependency warden – is installed.

Enabling Warden in your application

Next you have to register the Padrino::Warden module for your application. Just edit your app/app.rb file and near the other “register” lines add:

register Padrino::Warden

Configure how your model supports authentication

I assume that you already have a database model set up where you store usernames and passwords. In my application the model is called User and I use the email address as the identifier and a password. My model also has an “.authenticate” method that expects the email address and the password and either returns the user object (if login was successful) or nil/false (if login failed).

Once that is set up and worked (preferably with test code) you can add a file lib/authentication.rb (do not call it lib/warden.rb!) and make it look like this:

Warden::Strategies.add(:password) do
  def valid?
    params["email"] || params["password"]
  end

  def authenticate!
    if user = User.authenticate(params["email"], params["password"])
      success!(user)
    else
      fail!("Could not log in")
    end
  end
end

Warden::Manager.serialize_into_session { |user| user.id }
Warden::Manager.serialize_from_session { |id| User[id] }

Make sure that you customize the parts printed in bold letters for your purpose. The “valid?” method only tries the authentication if either of the fields is present. The “User. authenticate…” call should point to a method in your database model that returns the user if authentication was successful. Your method will likely search for the right user in the database based on the given email address and verify the password. And at the end change “user.id” into something that returns the ID (primary key) of that user record. Also turn “User[id]” into code that gets a certain user account from the database by its ID. “User[id]” works for Sequel. If you use ActiveRecord you will likely use something like “User.find(id)”.

Routes

Oh, by the way: padrino-warden adds a controller named “session” to your application. Just run “padrino rake routes” to see the extra routes you now have:

URL                               REQUEST  PATH
(:sessions, :unauthenticated)       GET    /unauthenticated
(:sessions, :unauthenticated)       PUT    /unauthenticated
(:sessions, :unauthenticated)      POST    /unauthenticated
(:sessions, :unauthenticated)     DELETE   /unauthenticated
(:sessions, :unauthenticated)     OPTIONS  /unauthenticated
(:sessions, :unauthenticated)      PATCH   /unauthenticated
(:sessions, :unauthenticated)      LINK    /unauthenticated
(:sessions, :unauthenticated)     UNLINK   /unauthenticated
(:sessions, :login)                 GET    /sessions/login [1]
(:sessions, :login)                POST    /sessions/login [2]
(:sessions, :logout)                GET    /sessions/logout [3]
(:sessions, :oauth, :callback)      GET    /sessions/oauth_callback

The most interesting routes are the ones I have marked [1], [2] and [3]. The process goes like this:

  1. The user gets redirected to /sessions/login and the application will send them an HTML form with a field for the email address (or username) and the password. This form sends it data per POST method to /sessions/login. Yes, the URL is the same – but the method has been GET at first and is now POST. The controller code for /sessions/login is already there (from padrino-warden). You just need to create the template file in app/views/sessions/login.slim (or login.erb or whatever template language you use) and create the form there. In SLIM it looks as simple as this:
    = form_tag url(:sessions_login), :method=>:post
      = email_field_tag :email
      = password_field_tag :password
      = submit_tag

  2. The user has pressed the submit button and the form gets sent to the POST method action of /sessoins/login which is also build into padrino-warden. So there is nothing to do for you here. If the authentication was unsuccessful the login page is shown again. If the authentication worked the user gets redirected to ‘/’.

  3. If the user decides to log out they can be linked to /sessions/logout which invalidates the login session.

Restricting controllers and actions

Of course your users won’t login voluntarily. You will have to restrict certain controllers or actions. This is rather simple. Either add these lines to make the entire controller require authentication:

  before do
    login
  end

Or just add the “logged_in? or login” expression at the beginning of the controller’s methods you want to secure.

Who is logged in?

padrino-warden provides a “user” (alias: “current_user”) variable for your templates that contains the actual user object according to your database model. So these lines (SLIM syntax) will show who is logged in or provide a login link:

- if logged_in?
  a href="#" Logged in as #{current_user.email}
- else
  a href="#" Anonymous

Objects

By the way these are the objects that padrino-warden gives you:

  • warden → Warden object
  • authenticate / login → render the login form
  • authenticated? / logged_in? → true if the user is logged in
  • logout → invalidate session
  • user / current_user → user object if a user is logged in

Authorization

Now I will have to start thinking about how to realize authorization… suggestions welcome. 🙂

4 thoughts on “How to use user authentication in your Padrino apps with padrino-warden”

    1. Christoph Haas

      In my opinion the advantage is the versatility. The kind of web applications I create are hardly ever the typical CRUD type. So you can assume I have more expectations of what a user model provides. Padrino's admin is a nice idea but I have quickly dropped it again. Reasons:

      • user model is very basic
      • no foreign key support in the web interface (you are supposed to know the referred row IDs)
      • no pagination support (see your browser die after opening the /admin on a model with 50,000 rows)

      I have discussed that with the Padrino administrators and it seems that the Admin component will be taken out of the core because it's not really ready for reality yet. Of course it will stay available as a plugin.

      Another grief I have with the built-in authentication/authorization module is this:

      foo/app.rb:
      
      access_control.roles_for :admin do |role|
        role.project_module :accounts, '/accounts'
        role.project_module :widgets, '/widgets'
      end

      You are putting restrictions on controllers in a central place. That's exactly the kind of centralized thinking that drove me away from Rails. I love specifying URL routes in the controllers instead of a central config/routes.rb file. Then why am I supposed to define access control in a central place?

      At the end of the day it's a matter of taste and the requirements of your web applications. But I really don't see any good use for the Admin component in my applications – at least not yet.

  1. I totally agree with you regarding the admin interface which comes with Padrino – it has no real practical use, especially when using associations.

    However, like you, I am trying to add authentication to my Padrino app, and I’m not seeing a lot of functionality there either. When I first starting pursuing this Ruby thing, I was excited to hear about the simplicity of Rails. I was especially excited about the Devise gem. Then I stumbled into Sinatra and Padrino. After crawling down a few rabbit holes, I found a few people suggesting Warden. Now, after actually integrating it into my app, I’m surmising that it’s more trouble than it’s worth. Does it really do that much which can’t be done by manipulating the session object? You’ve already created an authentication method, why not just create more methods to logout a user by destroying the session and the other minor functionality I’m seeing Warden providing?

    1. Christoph Haas

      Carl,

      you have a point there. Back in the days when I was using Pylons (a Python web framework with a similar concept as Padrino) there were several ways to do authentication. You could use additional modules. You could use WSGI middleware. Or you could roll your own. And in fact most developers just rolled their own layer of authentication just like you suggest.

      Comparing the two solutions:

      Aspect Padrino+Warden Roll your own
      Login form Need to build your own (and use certain form fields and targets) Need to build your own
      User model Need to build your own (and follow a certain API) Need to build your own
      Documentation Scattered and a bit confusing. (During the time you took to understand Padrino/Warden you probably have implemented your own.) You build it – you know how it works.
      Dependencies Padrino-warden is a one-man project that is not guaranteed to be there in a couple of years. Whatever you choose. Probably not any additional dependencies.
      Security Probably a bit more secure as others use and test it, too. You may overlook bugs that could be weaken the system.
      Forgot your password feature Need to build it on your own completely. Need to build it on your own completely.
      Protection against brute-force Need to build it on your own into the user model. Need to build it on your own into the user model.
      Enforcing authentication in controllers

      before do
        login
      end

      before do
        "get user from session"
        "check if session has expired"
        "redirect to login form unless session is okay"
      end

      (You could write helpers to do that in a lib/….rb place.)

      Authorisation No such functionality. Need to build your own. Need to build your own.
      Accessing who is logged in Automatically provided in ENV Need to check the cookie session.
      OpenID support I only found https://github.com/ursm/warden-openid which hasn't been maintained for 4 years ?

      There may be me more arguments but that's what just came up to my mind. Considering these aspects I would say that rolling your own is at least as good as using Padrino-Warden. I hoped to even that out by the cool authentication strategies for e.g. OpenID. But seeing that the recommended OpenID module for warden hasn't been touched in 4 years doesn't give me a good feeling.

      I also just realized that my bug report about a serious outdated gemon rubygems.org hasn't even been replied to. That does not leave me with a good feeling about a security-related dependency in my software. I will likely throw Padrino warden out.

      …Christoph

Leave a Reply to Christoph Haas Cancel reply

Your email address will not be published. Required fields are marked *

Scroll to Top