Seventh Octave

We're Starters. We write about culture, design and the business of software.

We believe software should be beautiful and inspired. Follow us as we build @TechOctave.

Authlogic and Rails 3.0 Solution TVD Nov 20

12 comments Latest by TVD

I believe solutions should be simple not simplistic. That's why I wasn't surprised when my client couldn't get the forked branch of Authlogic to work in his production Rails 3.0 environment. Well, I got Authlogic to work with the Official Branch and I'll show you how!

For those of you who are interested, he's running the Phusion Passenger for Nginx stack I recommend for hosted Rails applications.

Like you and me, my client is a developer, so I knew there had to be a problem if he switched to a fork'ed branch. But, still, switching to a forked branch just seemed too drastic to be the simple solution.

As an open source developer for Blogcast, this fork left me with nagging questions. Was the developer of Authlogic contacted about the issue? Has the developer abandoned Authlogic? If not, are we diverting support away from where it should be - the developer of Authlogic?

My gut tells me the answers to the above look something like: Maybe. No. Yes. Well, we all make mistakes. The best in us tries to learn from those mistakes. Time to move on!

The Problem

Some of you might be facing an issue with Authlogic 2.1.6 and Rails 3.0.3.

Gemfile

source 'http://rubygems.org'

gem 'rails', '3.0.3'
gem 'sqlite3-ruby', :require => 'sqlite3'
gem 'mysql2'
gem 'authlogic'

With a Gemfile that looks like that, you're running into an error that looks like this:

undefined method `to_key' for #<UserSession: no credentials provided>

What this means is your Authlogic authentication is failing. Why? It's failing because Authlogic's UserSession.new constructor is being called without passing a username and password:

app/controllers/UserSessionsController

def create
  @user_session = UserSession.new(params[:user_session])
  if @user_session.save
    redirect_to root_url
  else
    redirect_to admin_url
  end
end

How could this happen? Routing in Rails 3.0 is different. As a result, Authlogic can't do two things:

  1. First, Authlogic can't create a hash of your username/password and store it in :user_session, so params[...] will fail.
  2. Finally, Authlogic doesn't know which REST method to bind the authentication routine to, so it will fail to execute your create method.

My solution addresses each concern the Rails 3.0 way. As a result, I'll show you how to keep the Official Authlogic and the bonus is it will actually work in production.

The Unofficial Branch

First, I want to address the route many are purporting in the Rails Community. Many people are recommending we use the unofficial fork of Authlogic:

gem 'authlogic', :git => 'git://github.com/odorcicd/authlogic.git', :branch => 'rails3'

Essentially, they are suggesting you abandon the Authlogic core. I think such a decision is premature at best. At worst you're going to run into a host of support issues and I recommend against it! I'm not going to use the unofficial fork and I'll tell you why:

  1. It's unnecessary since Ben has not abandoned Authlogic
  2. The branch will not work with Bundler. Bundler installs git branches separately from system gems. So instead of seeing the correct install at /ruby/gems/1.8.7/gems/authlogic-2.1.6. You're going to see something like /.bundle/ruby/1.8.7/bundler/gems/authlogic-87e75311f835. What this means is Rails won't find Authlogic. That's a big problem!
  3. I have a more simple solution for you.

Think Simple not Simplistic

In discussing the problem, I mentioned two points of failure for Authlogic. My solution is to address the two points of failure so you can move on to better things like shipping your application.

Here, I'm going to show you the solution to getting Authlogic to play nice with Rail 3.0. Then I'm going to explain how my solution addresses each point of failure.

app/views/user_sessions/new.html.erb

This is a one line of code deal and it's clean. Very clean! So, just update your form_for helper.

This is how it might look originally:

<%= form_for @user_session do |f| %>

Update your form_for helper to this:

<%= form_for @user_session, :as => :user_session, :url => { :action => "create" } do |f| %>

The first problem was Authlogic couldn't create a hash of your username/password. Here, the solution is to create your own user session hash using Rails 3.0 syntax :as => :user_session.

Finally, Authlogic couldn't find which REST method to bind for authentication. So, tell Authlogic explicitly which method to execute :url => { :action => "create" }.

That's it! You're good to go!

Stock Options and Technical Debt

There is a second option that I don't recommend as I feel it adds technical debt to your code base. That option overwrites the to_key method in the UserSession model. You also must overwrite the persisted? method as well.

Do this and you don't have to update your new.html.erb View:

def to_key
  new_record? ? nil : [ self.send(self.class.primary_key) ]
end

def persisted?
  false
end

This option adds technical debt to the UserSession model since now you have to maintain Authlogic specific code. Such a move is best addressed by Authlogic's author.

I'm going to continue to support Ben Johnson because I believe Authlogic is a fine project for simple Authentication. I hope this solution gets you back on the Official Authlogic branch.

I know upgrading to Rails 3.0 is challenging. But I'm rooting for you and the entire Rails Community is rooting for you. So pick yourself up, dust yourself off and keep moving forward. Until next time Beloved, take care!

If you enjoyed this post, subscribe for updates (it's free).

Email Address:

12 comments so far

Pedro Nascimento 21 Nov 10

You really shouldn't just suggest 'ok, let's hack some stuff into the application'. Just fork the gem and make the appropriate changes. If you point bundler to it, everything works. Negligect bundler+git over proper Rubygems is too oldschool for me.

TVD 21 Nov 10

@Pedro Exactly! The simple solution is to use the proper Authlogic RubyGem. Look at http://railsplugins.org/plugins/56-authlogic and you will see an almost 50% failure rate. The failure rate is so high because no one stopped to diagnose the problem. We need to promote working within Rails 3.0 instead of advocating fork'ing and hacking. Imagine the chaos that would ensue if we fork'ed Rails every time there was a bug. The wise move is to commit a fix to the Official branch. Or better yet, join the Core Team. Then our collective energy will build better Open Source products, instead of leaving a trail of abandoned projects.

Mary Heintz 21 Nov 10

Thanks. This was very helpful. I had been using the forked version, but wanted to stick with the original. Your fix, and more importantly, your explanation helped a ton.

kris 25 Nov 10

The current master branch of Authlogic works fine in Rails 3. The gem just hasn't been updated, so simply pass :git => '...' to your gem call.

Sebastian 26 Nov 10

thank you very much for the fix. i tried it but i still get "undefined method `persisted?' for #" any idea what is wrong?

Tim 02 Dec 10

I followed these instructions and it works great on my local machine, but when I push to my Ubuntu server I receive "uninitialized constant ApplicationController::Authentication" Prior to switching I was using the git rails 3 branch method and was receiving the bundle error (... not checked out ) any ideas? thank you!

TVD 02 Dec 10

@Tim Does your Ubuntu server have the latest gem installs? Probably best to install the latest version of Rails w/ dependencies. Then bundle install to install your app's specific dependencies. If that doesn't work, I recommend checking out Stack Overflow to see if other developers have run into this issue. If you're the first, post your Question. The Rails community will help you and other developers will benefit from your question.

Ricardo Viana 18 Dec 10

Works for me. Great!! Thank you.

Gaelian Ditchburn 23 Dec 10

Just started a new Rails 3 project with Authlogic and was not liking the idea of using a fork. Thanks a lot for this. :)

TVD 24 Dec 10

@Gaelian I too did not like the idea of using a fork. There is a lot more that goes into writing an app than just code. We must consider architectural aesthetics and factor in longevity and maintainability. This happens from a high-level when looking at the specific app. But, it must also happen at lower-levels when looking at tools and plugins used in the application. For example, I can't tell you how many apps I've seen where the lead developer decided he could write a better jQuery and decided to fork. Years later, the app is stuck on jQuery 1.1.2 and can't take advantage of advances in the framework. This type of behavior smacks in the face of reason. As developers, we need to be mindful of ourselves and ensure longevity is a higher priority than short term gains.

Andrew 19 Jan 11

Also :) This is Rails 3. So, we do have ActiveModel. So just
class UserSession < Authlogic::Session::Base include ActiveModel::Conversion extend ActiveModel::Naming def persisted? false end end
will solve the problem. see Railscast #219

TVD 01 Feb 11

@Andrew Jolly good show old chap! This does indeed work. Save a line of code because you don't need to extend ActiveModel::Naming. I still think the Mixin and overwriting the persisted? method is best done in the Authlogic::Session::Base parent class.Then the UserSession model would simply inherit the needed methods and work as expected. That said, if my choice was your solution or the solution floated around in Stock Options and Technical Debt - I'd pick your solution hands down. This Mixin - include ActiveModel::Conversion is much cleaner than overwriting the to_key method.

Comments are closed