Authgasm is “rails authentication done right”
The last thing we need is another authentication solution for rails, right? That's what I thought. It was disappointing to find that all of the current solutions were overly complicated, bloated, poorly written, littered my application with code, and were just plain confusing. They felt very Microsoftish. This is not the simple / elegant rails we all fell in love with. It's like some Microsoft .NET engineers decided to dabble in ruby / rails for a day and their project was to write an authentication solution. That's what went through my head when I was trying out all of the current solutions. It's time someone makes a “rails like” authentication solution. So I give you Authgasm…
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…
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.save redirect_to account_url else render :action => :new end end def destroy @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 how about persisting the session…
class ApplicationController before_filter :load_user protected def load_user @user_session = UserSession.find @current_user = @user_session && @user_session.record end end
Authgasm makes this a reality. This is just the tip of the ice berg. Keep reading to find out everything Authgasm can do.
* Documentation: authgasm.rubyforge.org * Authgasm tutorial: coming soon… * Live example of the tutorial above (with source): coming soon.…
Bugs / feature suggestions: binarylogic.lighthouseapp.com/projects/18752-authgasm
Install and use
Install the gem / plugin
$ sudo gem install authgasm $ cd vendor/plugins $ sudo gem unpack authgasm
Or as a plugin
script/plugin install git://github.com/binarylogic/authgasm.git
Create your session
For this walk through lets assume you are setting up a session for your User model.
Create your user_session.rb file:
# app/models/user_session.rb class UserSession < Authgasm::Session::Base # configuration here, just like ActiveRecord, or in an initializer # See Authgasm::Session::Config::ClassMethods for more details end
It is important to set your configuration for your session before you set the configuration for your model. This will save you some time. Your model will try to guess its own configuration based on what you set in the session. These are completely separate, making Authgasm as flexible as it needs to be, but the majority of the time they will be the same and no one likes to repeat their self.
Ensure proper database fields
The user model needs to have the following columns. The names of these columns can be changed with configuration.
t.string :login, :null => false t.string :crypted_password, :null => false t.string :password_salt, :null => false # not needed if you are encrypting your pw instead of using a hash algorithm t.string :remember_token, :null => false t.integer :login_count # This is optional, it is a "magic" column, just like "created_at". See below for a list of all magic columns.
Set up your model
Make sure you have a model that you will be authenticating with. For this example let's say you have a User model:
class User < ActiveRecord::Base acts_as_authentic # for options see documentation: Authgasm::ActsAsAuthentic::ClassMethods end
Done! Now go use it just like you would with any other ActiveRecord model (see above).
Just like ActiveRecord has “magic” columns, such as: created_at and updated_at. Authgasm has its own “magic” columns too:
Column name Description login_count Increased every time an explicit login is made. This will *NOT* increase if logging in by a session, cookie, or basic http auth last_request_at Updates every time the user logs in, either by explicitly logging in, or logging in by cookie, session, or http auth current_login_at Updates with the current time when an explicit login is made. last_login_at Updates with the value of current_login_at before it is reset. current_login_ip Updates with the request remote_ip when an explicit login is made. last_login_ip Updates with the value of current_login_ip before it is reset.
Authgasm tries to check the state of the record before creating the session. If your record responds to the following methods and any of them return false, validation will fail:
Method name Description approved? Has the record been approved? confirmed? Has the record been conirmed? inactive? Is the record marked as inactive?
What's neat about this is that these are checked upon any type of login. When logging in explicitly, by cookie, session, or basic http auth. So if you mark a user inactive in the middle of their session they wont be logged back in next time they refresh the page. Giving you complete control.
Need Authgasm to check your own “state”? No problem, check out the hooks section below. Add in a before_validation or after_validation to do your own checking.
Hooks / Callbacks
Just like ActiveRecord you can create your own hooks / callbacks so that you can do whatever you want when certain actions are performed. Here they are:
before_create after_create before_destroy after_destroy before_save after_save before_update after_update before_validation after_validation
The errors in Authgasm work JUST LIKE ActiveRecord. In fact, it uses the exact same ActiveRecord errors class. Use it the same way:
class UserSession before_validation :check_if_awesome private def check_if_awesome errors.add(:login, "must contain awesome") if login && !login.include?("awesome") errors.add_to_base("You must be awesome to log in") unless record.awesome? end end
Automatic Session Updating
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.
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 Authgasm can, and you can access Authgasm 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 how this is done.…
class User < ActiveRecord::Base after_create :create_sessions! after_update :update_sessions! private def create_sessions! # create a new UserSession if they are not logged in end def update_sessions! # find their session # check that their session's record is the same one as this one: session.record == self # update the session with the new info: session.update end end
Obviously there is a little more to it than this, but hopefully this clarifies any confusion. Lastly, this can be altered / disabled via a configuration option.
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!
Multiple Sessions / Session Identifiers
You're asking: “why would I want multiple sessions?”. Take this example:
You have an app where users login and then need to re-login to view / change their billing information. Similar to how Apple's me.com works. What you could do is have the user login with their normal session, then have an entirely new session that represents their “secure” session. But wait, this is 2 users sessions. No problem:
# regular user session @user_session = UserSession.new @user_session.id # => nil # secure user session @secure_user_session = UserSession.new(:secure) @secure_user_session.id # => :secure
This will keep everything separate. The :secure session will store its info in a separate cookie, separate session, etc. Just set the id and you are good to go. Need to retrieve the session?
@user_session = UserSession.find @secure_user_session = UserSession.find(:secure)
For more information on ids checkout Authgasm::Session::Base#initialize
What about [insert framework here]?
As of now, authgasm supports rails right out of the box. But I designed authgasm to be framework agnostic. The only thing stopping Authgasm from being implemented in merb, or any other framework, is a simple adapter. I have not had the opportunity to use Authgasm in anything other than rails. If you want to use this in merb or any other framework take a look at authgasm/controller/rails_adapter.rb.
How it works
Interested in how all of this all works? Basically a before_filter is automatically set in your controller which lets Authgasm know about the current controller object. This allows Authgasm to set sessions, cookies, login via basic http auth, etc. If you are using rails in a multiple thread environment, don't worry. I kept that in mind and made this thread safe.
From there it is pretty simple. When you try to create a new session the record is authenticated and then all of the session / cookie magic is done for you. The sky is the limit.
Copyright © 2008 Ben Johnson of [Binary Logic](www.binarylogic.com), released under the MIT license