Tatva-Artha

meaning of "it"

Authlogic: after the initial hype

without comments

On my rails projects, I’ve used restful_authentication before and I am using authlogic now. Even though I have passed the initial hesitation phase with authlogic, I can’t say that I am totally sold.

What I like about authlogic is it refrains from providing any controller/view level support and handles model layer better. Instead, it provides a solid model functionality that is similar in principle to ActiveRecord and provides lots of how-to examples on how you might code your controllers/views/workflows. It does it well. Although authlogic is complemented for refraining from too much code generation, Authlogic still does a lot of magical stuff. You encounter this readily with tests. Instantiating and persisting a user will log you in! It is hard to test your User model from console since authlogic will fail if you try to instantiate User object in console. You have to jump thru hoops (include Authlogic, set proper controller reference) to get it to work.

Problem with Perishable Token:
Today I encountered another issue with authlogic realted to perishable token. According to docs, perishable tokens are useful for implementing features such as reset-password and confirm email. These functionalities are essentially similar in implementation in that application generates a token and emails url containing this token to the user’s registered email address. User clicks on the url and resets his password or confirms that email is indeed his email. The implementation details are explained very well by author here.

However, it didn’t work in my case. The problem, I believe, is that perishable_token changes often… too often actually. Looking side the codebase for Authlogic::ActsAsAuthentic::PerishableToken reveals this:

def self.included(klass)
  return if !klass.column_names.include?("perishable_token")
  klass.class_eval do
    extend ClassMethods
    include InstanceMethods
 
    before_save :reset_perishable_token, :unless => :disable_perishable_token_maintenance?
  end
end
def reset_perishable_token
  self.perishable_token = Random.friendly_token
end

As you can see, the token changes on each save. This can be readily verified from console:

>> u = User.find_by_email("sjain@iit.edu")
#=> #<User id: 1, email: "sjain@iit.edu", perishable_token: "6qLHkU_vBQK6rLQGy-oL", login_count: 54, failed_login_count: 0, last_request_at: "2009-09-30 18:00:46", current_login_at: "2009-09-30 18:00:42", last_login_at: "2009-09-30 18:00:42", current_login_ip: "127.0.0.1", last_login_ip: "127.0.0.1"...>
>> u.perishable_token
#=> "6qLHkU_vBQK6rLQGy-oL"
>> u.save
#=> true
>> u.perishable_token
=> "1tlcpvc_sCGtNEfIQCt1"
>>

Everytime the user object is saved, the token changes. In a running app this could happen for any number of reasons. I wonder, if having other authlogic magic attributes like login_count, last_request_at etc. causes the instance to get saved immediately after the token was sent in email, practically overwriting the token that was critical to password-reset workflow…

Solution:
Fortunately, Authlogic allows you to take control and manage token yourself. To do this, you disable the automatic maintenance of perishable_token by authlogic as follows:

class User < ActiveRecord::Base
  acts_as_authentic
  disable_perishable_token_maintenance(true)
end

By doing this, authlogic leaves this column in database alone. You can now reset the token when you need it. Here is what my password-reset and confirm-email hooks look like then:

class User < ActiveRecord::Base
  def deliver_confirm_email_instructions!
    reset_perishable_token!
    Notifier.deliver_confirm_email_instructions(self)
  end
 
  def deliver_password_reset_instructions!
    reset_perishable_token!
    Notifier.deliver_password_reset_instructions(self)
  end
end

In addition, since we took the control of perishable_token in our hands and that it is a non-nullable field in database, we have to reset the token when a new user registers for an account on our website.

class UsersController < ApplicationController
  def create
    @user = User.new(params[:user])
    # set the token manually
    @user.reset_perishable_token
    if @user.save
      @user.deliver_confirm_email_instructions!
      flash[:notice] = "Registration successful. Please check your email to confirm your email address."
      redirect_to root_url
    else
      render :action => 'new'
    end
  end
end

Conclusion:
All in all, authlogic is a well designed authentication library. However, you can’t get around the fact that authentication and its surrounding sub-features (password reset, registration, email confirmation) can get complex. When coming away from restful_authentication to authlogic you will be glad to find intuitive for its API that is very similar to ActiveRecord, configurablility to enable/disable features that you need and don’t need. But, don’t expect an easy ride. You are bound to have to dive into the codebase to take advantage of some of the complex features.

http://www.tatvartha.com/wp-content/plugins/sociofluid/images/digg_16.png http://www.tatvartha.com/wp-content/plugins/sociofluid/images/reddit_16.png http://www.tatvartha.com/wp-content/plugins/sociofluid/images/stumbleupon_16.png http://www.tatvartha.com/wp-content/plugins/sociofluid/images/delicious_16.png http://www.tatvartha.com/wp-content/plugins/sociofluid/images/google_16.png http://www.tatvartha.com/wp-content/plugins/sociofluid/images/twitter_16.png

Related posts:

  1. Working with Authlogic in script/console When probing / debugging issues with Authlogic on rails console,...
  2. Ruby Authlogic: lazy initialization based on defined?() call What is the difference between following two versions of code?...
  3. Rails Email Unit Testing ActionMailer classes, for sending out emails, typically reside with...

Related posts brought to you by Yet Another Related Posts Plugin.

Written by Sharad

September 30th, 2009 at 8:33 pm

Posted in All

Leave a Reply