The last thing we need is another authentication solution for rails, right? That's what I thought until I tried out some of the current solutions. None of them felt right. They were either too complicated, bloated, littered my application with tons of code, or were just confusing. This is not the simple / elegant rails we all fell in love with. We need a "rails like" authentication solution. Authlogic is my attempt to satisfy that need...

What if you could have authentication up and running in minutes without having to run a generator? All because it's simple, like everything else in rails.

What if creating a user session could be as simple as...

UserSession.create(params[:user])

What if your user sessions controller could look just like your other controllers...

class UserSessionsController < ApplicationController
  def new
    @user_session = UserSession.new
  end

  def create
    @user_session = UserSession.new(params[:user_session])
    if @user_session.create
      redirect_to account_url
    else
      render :action => :new
    end
  end

  def destroy
    current_user_session.destroy
  end
end

Look familiar? If you didn't know any better, you would think UserSession was an ActiveRecord model. I think that's pretty cool. Why is that cool? Because it fits nicely into the RESTful development pattern and its a style we all know and love. Wouldn't this be cool too...

<%= error_messages_for "user_session" %>
<% form_for @user_session do |f| %>
  <%= f.label :login %><br />
  <%= f.text_field :login %><br />
  <br />
  <%= f.label :password %><br />
  <%= f.password_field :password %><br />
  <br />
  <%= f.submit "Login" %>
<% end %>

Or what about persisting the session...

class ApplicationController
  helper_method :current_user_session, :current_user

  protected
    def current_user_session
      return @current_user_session if defined?(@current_user_session)
      @current_user_session = UserSession.find
    end

    def current_user
      return @current_user if defined?(@current_user)
      @current_user = current_user_session && current_user_session.user
    end
end

Authlogic makes this a reality.

Reclaim your UsersController

This is one of my favorite features that I think its pretty cool. It's things like this that make a library great and let you know you are on the right track.

Just to clear up any confusion, Authlogic does not store the plain id in the session. It stores a token. This token changes with the password, this way stale sessions can not be persisted.

That being said..What if a user changes their password? You have to re-log them in with the new password, recreate the session, etc, pain in the ass. Or what if a user creates a new user account? You have to do the same thing. Here's an even better one: what if a user is in the admin area and changes his own password? There might even be another place passwords can change. It shouldn't matter, your code should be written in a way where you don't have to remember to do this.

Instead of updating sessions all over the place, doesn't it make sense to do this at a lower level? Like the User model? You're saying "but Ben, models can't mess around with sessions and cookies". True...but Authlogic can, and you can access Authlogic just like a model. I know in most situations it's not good practice to do this but I view this in the same class as sweepers, and feel like it actually is good practice here. User sessions are directly tied to users, they should be connected on the model level.

Fear not, because the acts_as_authentic method you call in your model takes care of this for you, by adding an after_create and after_update callback to automatically keep the session up to date. You don't have to worry about it anymore. Don't even think about it. Let your UsersController deal with users, not users AND sessions. ANYTIME the user changes his password in ANY way, his session will be updated.

Here is basically what is done...

class User < ActiveRecord::Base
  after_save :maintain_sessions!

  private
    def create_sessions!
      # create a new UserSession if they are not logged in
    end

    def maintain_sessions!
      # If we aren't logged in at all and the password was changed, go ahead and log the user in
      # If we are logged in and the password has change, update the sessions
    end
end

Obviously there is a little more to it than this, but hopefully this clarifies any confusion.

When things come together like this I think its a sign that you are doing something right. Put that in your pipe and smoke it!

Authlogic arouses me

That's great, here are some resources:

28 Responses to “Authlogic released! Rails authentication done right.”

  1. October 26th, 2008 at 09:09 PM John Says

    Great work Ben. This is really cool, as is Searchgasm. But why are you using the “gasm” suffix for these? At the risk of getting flamed for being a retard, I can’t see myself using these plugins at work only because of the names. I know that sounds dumb, but I work for a fortune 500 and it would be a little awkward if I had to mention that we have an issue with the “Authgasm” plugin during a meeting. :-) I might be in the minority here, but I doubt I’m the only one who feels this way.

    Any chance of reconsidering the naming convention on these plugins? :-)

    Cheers, John

  2. October 26th, 2008 at 09:48 PM David Says

    ummm, cool! I like this a lot.

  3. October 26th, 2008 at 10:39 PM Ben Johnson Says

    Ha ha, thanks for your comments John. I can’t believe you don’t like the name. I spent hours coming up with it.

    Honestly, I just thought it was funny, and I thought it was funny for that exact reason you mentioned. I could envision people bringing this up in discussions. I decided to use the name searchgasm and authgasm, because:

    • It was different, and I really didn’t care what the name was. After all, it’s just a name.
    • How can you forget the name?
    • I felt like all of the names were these serious “enterprise” names. I wanted something different that would get people’s attention, and maybe lighten the mood a little bit.

    Regardless, it’s all in the delivery. If you don’t want to say “authgasm” in a meeting. Just say “binary logic’s authentication solution” or say “authentication solution”, or [insert enterprise appropriate name here].

    Also, I went with authgasm for consistency and the fact that I really don’t care what it’s called. Just wanted something different than some ole boring name. How else am I going to get people’s attention on a plugin that deals with a solution that’s been “solved” a million times? Maybe once I get people’s attention I’ll change it. Got any suggestions?

  4. October 27th, 2008 at 02:00 AM Shamiq Says

    RADR- pronounced RADAR.

    Rails Authentication Done Right.

  5. October 27th, 2008 at 06:07 AM Mathias Says

    It all sounds good, but no test cases? Seriously? For something as important as authentication? That’s a total no-go for me.

  6. October 27th, 2008 at 06:13 AM Edo Says

    Nice, looks really good!

    John, what a ridiculous sample of american puritanism. All my prejudices about the strange country across the atlantic suddenly are nothings but true. ;)

  7. October 27th, 2008 at 08:09 AM AkitaOnRails Says

    Actually, I would call “authentication done right” for a plugin that leveraged out-of-the-box LDAP integration. I know, I am guilt too because I never contributed a LDAP solution. Does anyone know any authentication solution that already does that? Meaning: authenticates against Active Directory, for instance?

  8. October 27th, 2008 at 08:51 AM Frank Booth Says

    Why is the password being dragged around in the user’s session?

    Why worry about when a use changes their password?

    Seems like extra work to me.

    An authn’ed user is authn’ed until they log out. If they changed their password during their last session, then they obviously need to log back in using their new password.

    FB.

  9. October 27th, 2008 at 11:41 AM Ben Johnson Says

    Mathais, that is why this is still in beta (0.X). I completed the tests last night and am about to release 0.10.0. And it’s not like I haven’t been testing this. Which is why I have no problems with any of the tests I wrote. The only way to test this is in a rails app. Rails provides ways to easily perform functional tests. Anyways, it’s all good now.

    Frank, I’m confused. The password isn’t being dragged around. In fact, the password is not publicly accessible, period. When logging in via a session or a cookie a password isn’t even provided. If you don’t mind, please let me know what you are talking about. Thanks!

  10. October 27th, 2008 at 01:34 PM Frank Booth Says

    Frank, I’m confused. The password isn’t being dragged around. In fact, the password is not publicly accessible, period. When logging in via a session or a cookie a password isn’t even provided. If you don’t mind, please let me know what you are talking about. Thanks!

    I refer to the exxerpt below: “What if a user changes their password? You have to re-log them in with the new password, recreate the session, etc, pain in the ass. Or what if a user creates a new user account? You have to do the same thing. Here’s an even better one: what if a user is in the admin area and changes his own password? There might even be another place passwords can change. It shouldn’t matter, your code should be written in a way where you don’t have to remember to do this.”

    From that, I inferred that you were accounting to keeping the changed password synced back into the user record or session record. I made this inference based on the fact that you devoted more than a paragraph to the idea that a user loses their logged-in status by changing their password.

    FB

  11. October 27th, 2008 at 01:43 PM Ben Johnson Says

    Not a problem Frank. Yeah, the password is not kept around or stored anywhere. The only place it is stored is in the “crypted_password” field where it is…encryped, either via a hash or an encryption algorithm, based on your settings. All that I was talking about in that paragraph is updating the session and the “remember_token”, which is what the cookie holds.

  12. October 27th, 2008 at 02:32 PM Frank Booth Says

    What I’m saying is that the act of resetting a password is just a database update. I don’t see why you’d need or want to

    “re-log them in with the new password, recreate the session, etc.”

    I was asking where the need to take such drastic steps came from.

    FB

  13. October 27th, 2008 at 04:50 PM Ben Johnson Says

    Frank, I see what you are saying.

    • if they wanted to be remembered for the next 3 months or so, their cookie is no longer valid. During their next session they will need to re-login, which doesn’t make sense, since they are the ones that changed the password.
    • Right now the session stores the user_id. Pretty much all of the rails authentication solutions do this. I don’t think this is smart. What if 2 people are logged into the account at the same time? Let’s pretend one of them shouldn’t be logged into that account. If one of the users knows this and tries to change the password the other user should be logged out. Otherwise the session could be persisted forever and the other user account would never be logged out. I am going to change this now.

    That being said you would need to update the session and the cookie with the new “token”.

  14. October 28th, 2008 at 06:51 AM Preston Marshall Says

    Do you think you might be able to post something about how to integrate roles into this system? I think roles are something thats severely lacking in the rails community.

  15. October 28th, 2008 at 07:17 AM Daniel Schierbeck Says

    Why not use an observer to clear the sessions, instead of polluting the User model?

  16. October 28th, 2008 at 11:58 AM Ben Johnson Says

    Hey Preston, you make a good point. But roles really have to do with the user their self, not really their session. All of the role systems I have implemented were extremely simple. I was under the impression there were some pretty good role plugins. Have you checked out the recipes in the “rails recipes” books. They have a good chapter on this.

    Daniel, I agree 100%. But I am doing this from a library stand point. Making you create a UserObserver, adding it to your config.observers, then including an authgasm module is a lot more work than just calling acts_as_authentic in your User model. Observers are a way for you to organize your code. acts_as_authentic doesn’t “generate” more code, therefore it really doesn’t matter if its in an observer or the model, it’s all the same to you.

  17. October 29th, 2008 at 04:20 PM douglivesey Says

    For roles, check out ACL2—really simple & effective: http://opensvn.csie.org/ezra/rails/plugins/dev/acl_system2/

  18. October 30th, 2008 at 10:42 AM Daniel Says

    I like the name. Keep it. If someone is too much of a sissy-la-la to use a tool just because of their name, that is their problem. I think keeping humor in the workplace is a good thing, and its an attribute of an agile programer. Agile like ninja. Agile ninjas have the ability to cut you down if you don’t like their naming conventions…..

    Keep up the good work, as I am too busy having an orgasm from reading your Authgasm code.

  19. November 1st, 2008 at 04:19 AM Umair Says

    What if users are divided into different accounts? For example, in case of SaaS, where an account is identified by the subdomain name. By a quick glance at the code, it seems that logins and email addresses wlll be unique through out the table. How about adding :scope_id for login and email address?

  20. November 1st, 2008 at 04:59 AM Umair Says

    Something like

    acts_as_authentic :scope_id => :account

  21. November 1st, 2008 at 11:28 AM Ben Johnson Says

    Umair, you make a good point. I will add that in.

  22. November 2nd, 2008 at 03:43 PM Alex Says

    Ben, I assume many people will attempt to replace their restful_authentication with authgasm. I’d therefor suggest to focus on the differences between these two (are openid, verification emails, reset password email,... planned for authgasm?), plus adding a quick how-to for a migration. E.g. restful_auth created some conventions and convenience methods, such as current_user (but maybe other stuff too I can’t think of atm), and I doubt people really know what they’d need to do if they want to get rid of restful_authentication over the long term…

  23. November 3rd, 2008 at 01:34 AM Ben Johnson Says

    Umair, I released an update and hardcore scoped support is now added. I think its pretty nifty.

    Alex, you make a really good point. I thought about all of those things, so here are my thoughts on each one:

    1. OpenID: I do plan to add this in. This should be very simple and authlogic makes it easy.

    2. Verification emails, reset password email: I feel like emails are really up to the application. The app might want to style them in a certain way, might want to reset passwords different, etc. I actually started a tutorial on how to reset passwords with authgasm. I just did it in a project of mine and it was extremely easy and simple. Authlogic has everything in place to make this easy. I also feel like that is something your app should handle, not a authlogic. They are fairly elementary tasks, and my tutorial should clarify this.

    But you make some good points and I added “switching from restful_authentication” to my list of blog posts to do. Hopefully I can get that up this week, since it should be rather simple.

    Thanks for your comments.

  24. November 3rd, 2008 at 10:08 AM Umair Says

    Thanks Ben, I can see you have made some well thought out changes. I’m going to try them out now. Will be helpful for an upcoming project.

    I’m also glad for the name changes :)

  25. November 28th, 2008 at 09:30 PM Avram Says

    Hello,

    The plugin command is not working for me:

    % script/plugin install git://github.com/binarylogic/authlogic.git Plugin not found: [“git://github.com/binarylogic/authlogic.git”] %

    And I don’t seem to be able to use the gem version either b/c my version of rails complains that the config.gem method doesn’t exist.

    Can someone kindly help me? Thanks!

  26. December 18th, 2008 at 04:43 PM mabed Says

    From looking at example demo site you have. Changing the password doesn’t require typing the old password. I was wondering, what I need to do to retrieve the saved password and compare it to the old password just typed. Thanks.

  27. December 19th, 2008 at 04:29 PM Ben Johnson Says

    Mabed, if you look in the docs for acts_as_authentic there is a valid_password?(password_hash) method you can use.

  28. December 20th, 2008 at 09:43 PM Mabed Says
    Thank Ben, I am new to Ruby and RoR. I have done this implementation, of change_password. I created a change_password_Controller. Below is the update which works for the most part except if the new password & the conformation are different, int that case, if gives a cryptic “Template is missing – Missing template passwords/update.erb in view path /Users/mabed/hifimaven/app/views:”

    def update if current_user.valid_password?(params[:old_password]) current_user.password = params:user current_user.password_confirmation = params:user begin current_user.save! flash[:notice] = ‘Successfully changed your password.’ redirect_to account_url rescue ActiveRecord::RecordInvalid => e flash[:error] = “Couldn’t change your password: #{e}” end else flash[:notice] = “Couldn’t change your password: Either you’re old password is incorrect or the new password and confirmation aren’t the same” render :action => :edit end end

Leave a Reply