Adventures with Ruby

AuthLogic is awesome!

View Comments

Probably every Rails developer has used restful_authentication. Most of us on practically every application. But restful_authentication generates quite some code and is somewhat an odd plugin. It is a bit out of place in the regular restful controllers that you make. They are making a new modular version of it, but it isn’t much of an improvement, if you ask me.

Luckily, there is a new plugin/gem that does this better. It’s called AuthLogic. It used to be called AuthGasm, but now we can actually install it without blushing every time we do ls vendor/plugins. It gives you a familiar acts_as_authentic for your user model (nothing more, mind you!) and a UserSession model, which isn’t inheriting from ActiveRecord, but from AuthLogic::Session::Base class, which AuthLogic provides.

The big plus is that now you can use form_for for user sessions just as you would for any ActiveRecord model. Your UserSessionsController can even be like rails scaffold provides. Just save to the @user_session object and a session is set.

You’re free to make multiple sessions per user to be able to log in on multiple locations. Just read the README to see what is possible!

There are no specs provided with AuthLogic, so here are some helpers to spec controllers. Add them to spec_helper.rb or in another file which gets loaded by RSpec.

def current_user(stubs = {})
  @current_user ||= mock_model(User, stubs)
end

def user_session(stubs = {}, user_stubs = {})
  @current_user ||= mock_model(UserSession, {:user => current_user(user_stubs)}.merge(stubs))
end

def login(session_stubs = {}, user_stubs = {})
  UserSession.stub!(:find).and_return(user_session(session_stubs, user_stubs))
end

def logout
  @user_session = nil
end

So you can write specs like this:

describe SecretsController do
  before { login }
  it "should be very very secret!"
end

And as a litte bonus, it works nice with my plugin: acts_as_translatable_model. Making your login forms is easier than ever! So please take a look at it! I’m betting you’ll love it!

Written by Iain Hecker

November 13th, 2008 at 9:17 pm

Posted in iain.nl

  • http://www.code-schubser.de/ Jan

    Authlogic IS awesome! So is cucumber. If only I would be able to use them together.. Did you ever try this? I don’t know how to stay logged in.

  • http://www.code-schubser.de/ Jan

    Authlogic IS awesome! So is cucumber. If only I would be able to use them together.. Did you ever try this? I don’t know how to stay logged in.

  • http://www.jbpros.net Julien Biezemans

    Thank you for the rspec helpers!

    I’m using your helpers but I think there is an error. Shouldn’t user_session method store the mocked model into @user_session instead of @current_user?

    def user_session(stubs = {}, user_stubs = {})
      @user_session ||= mock_model(UserSession, {:user => current_user(user_stubs)}.merge(stubs))
    end

    Well, it’s working better in here.

  • http://www.jbpros.net Julien Biezemans

    Thank you for the rspec helpers!

    I’m using your helpers but I think there is an error. Shouldn’t user_session method store the mocked model into @user_session instead of @current_user?

    def user_session(stubs = {}, user_stubs = {})
      @user_session ||= mock_model(UserSession, {:user => current_user(user_stubs)}.merge(stubs))
    end

    Well, it’s working better in here.

  • http://detersmart.dk Mikker

    I’m using these and they work great. But I’d like a method that makes the logged in user an admin (the User model has a method ‘admin?’). I’ve tried some things, but nothing has worked yet. How would you go about that?

    Thanks!

  • http://detersmart.dk Mikker

    I’m using these and they work great. But I’d like a method that makes the logged in user an admin (the User model has a method ‘admin?’). I’ve tried some things, but nothing has worked yet. How would you go about that?

    Thanks!

  • Josh Pencheon

    @Mikker,

    I too have been trying to do that, also without much success. I gave up in the end, but I think this may have worked:

    ActionController.stubs(:current_user).returns(mock_model(User,{:admin => true}))
  • Josh Pencheon

    @Mikker,

    I too have been trying to do that, also without much success. I gave up in the end, but I think this may have worked:

    ActionController.stubs(:current_user).returns(mock_model(User,{:admin => true}))
  • chuy

    can you post a sample using specs?
    i am trying to spec my controller that uses the session id to get the users information. How do you use the helper methods from your controller_spec.rb?

    thx

  • chuy

    can you post a sample using specs?
    i am trying to spec my controller that uses the session id to get the users information. How do you use the helper methods from your controller_spec.rb?

    thx

  • http://detersmart.dk Mikker

    This is what I ended up doing. I’m not completely sure if I’m all crazy about it. But it seems to kinda work:

    def login(factory = :user)
      @current_user = Factory(factory)
      @current_user_session = mock_model(UserSession, {:record => current_user})
      UserSession.any_instance.stubs(:find).returns(current_user_session)
      controller.stubs(:current_user).returns(current_user)
    end
     
    def admin_login
      login(:admin)
    end

    Using padlock for authorization and factory_girl for factories.

    Factory.define :user do |u|
      u.sequence(:email) { |n| "bangbang_#{n}@example.com" }
      u.password 'foobar0123'
      u.password_confirmation 'foobar0123'
    end
     
    Factory.define :admin, :parent => :user do |a|
      a.roles { [Factory(:admin_role)] }
    end
     
    Factory.define(:admin_role, :class => Role) do |role|
      role.name 'admin'
      role.authorizable { Factory(:app) }
    end
  • http://detersmart.dk Mikker

    This is what I ended up doing. I’m not completely sure if I’m all crazy about it. But it seems to kinda work:

    def login(factory = :user)
      @current_user = Factory(factory)
      @current_user_session = mock_model(UserSession, {:record => current_user})
      UserSession.any_instance.stubs(:find).returns(current_user_session)
      controller.stubs(:current_user).returns(current_user)
    end
     
    def admin_login
      login(:admin)
    end

    Using padlock for authorization and factory_girl for factories.

    Factory.define :user do |u|
      u.sequence(:email) { |n| "bangbang_#{n}@example.com" }
      u.password 'foobar0123'
      u.password_confirmation 'foobar0123'
    end
     
    Factory.define :admin, :parent => :user do |a|
      a.roles { [Factory(:admin_role)] }
    end
     
    Factory.define(:admin_role, :class => Role) do |role|
      role.name 'admin'
      role.authorizable { Factory(:app) }
    end
  • Jim

    I have been using Authlogic successfully on a prototype of a website that I am creating, but am a little concerned about the behavior of the authentication system with regard to the browser cache. It seems that after I have logged a user in, and then log out, I can hit the back button and go right back to the last logged-in page because the browser stored it in the cache. Is there an easy way to avoid this?

  • Jim

    I have been using Authlogic successfully on a prototype of a website that I am creating, but am a little concerned about the behavior of the authentication system with regard to the browser cache. It seems that after I have logged a user in, and then log out, I can hit the back button and go right back to the last logged-in page because the browser stored it in the cache. Is there an easy way to avoid this?

  • http://glenn-roberts.com GlennR

    Juliens solution worked too.

    I found I needed to stub the :record method too ;

    def user_session(stubs = {}, user_stubs = {})
      @user_session ||= mock_model(UserSession, {:user => current_user(user_stubs), :record => true}.merge(stubs))
    end
    
  • http://glenn-roberts.com GlennR

    Juliens solution worked too.

    I found I needed to stub the :record method too ;

    def user_session(stubs = {}, user_stubs = {})
      @user_session ||= mock_model(UserSession, {:user => current_user(user_stubs), :record => true}.merge(stubs))
    end
    
  • http://iain.in/ Iain

    Authlogic is amazing for authentication, it simplifies and provides a nice, unobtrusive library for rails applications.
    And I happened to across this post, thinking of writing tests for it.
    Thanks for the nice blog.

  • http://iain.in/ Iain

    Authlogic is amazing for authentication, it simplifies and provides a nice, unobtrusive library for rails applications.
    And I happened to across this post, thinking of writing tests for it.
    Thanks for the nice blog.

  • Amiruddin Nagri

    I am definitely late for the party, a year late, anyways.

    I am using authlogic 2.1.5.
    When I try to use the above sample code, it blows out giving the error message

    undefined method `has_attribute?’ for #
    /home/xuser/myprojects/xproject/spec/controllers/users_controller_spec.rb:13:in `user_session’
    /home/xuser/myprojects/xproject/spec/controllers/users_controller_spec.rb:17:in `login’
    /home/xuser/myprojects/xproject/spec/controllers/users_controller_spec.rb:58:

    Looks like the session implementation has been changed in the latest version. Any awesome solution from the awesome-authlogic to avoid this ?

  • Amiruddin Nagri

    I am definitely late for the party, a year late, anyways.

    I am using authlogic 2.1.5.
    When I try to use the above sample code, it blows out giving the error message

    undefined method `has_attribute?’ for #
    /home/xuser/myprojects/xproject/spec/controllers/users_controller_spec.rb:13:in `user_session’
    /home/xuser/myprojects/xproject/spec/controllers/users_controller_spec.rb:17:in `login’
    /home/xuser/myprojects/xproject/spec/controllers/users_controller_spec.rb:58:

    Looks like the session implementation has been changed in the latest version. Any awesome solution from the awesome-authlogic to avoid this ?

blog comments powered by Disqus
Fork me on GitHub