Restful Authentication for Ruby on Rails Apps
Posted by Vince Wadhwani on Mar 05, 2007
Update: May 2007 Fixed a snag for activation.
I recently started rewriting one of my sites to be RESTful. The first step for me was to get authentication working. In the past I relied on Rick Olsen's Acts as Authenticated plugin to do the work for me. It was fabulous. But new times call for new methods and now being RESTful is the way to go. There is a Restful Authentication plugin but it's not nearly as well documented and there are some small issues which may confuse you. Allow me to save you some time by showing you a few of the roadblocks I ran into.
First of all, you'll need to add a few things to your environment.rb file. Within the Rails::Initializer you'll have to add code that says:
config.active_record.observers = :user_observer
Failing to do that means that your emails won't get sent out. Adding it outside the Rails::Initializer and you'll get an error when starting your script/server so be careful you put it before the end!
Next up, after the part of the environment.rb where you add your own configuration, make sure you have your method of sending mail down. I use sendmail which means that my line is simply:
ActionMailer::Base.delivery_method=:sendmail
With Rails 1.2 there are now some extra options you can set for SMTP or Sendmail in your environment.rb. Check Ryan Daigle's site for a nice post on this.
There's one more absolutely essential thing that you must fix before Restful Authentication will work. By default, in the users_controller.rb file, there is an action called activate which first finds the user to activate according to his or her email address. That line looks like this:
self.current_user = User.find_by_activation_code(params[:email])
Now for some reason that did not work for me. If it works for you that's great.. perhaps there's a bug somewhere in my installation or something. In any case, changing the find method allowed things to move smoothly once more:
self.current_user = User.find_by_activation_code(params[:id])
Lastly, there are some routes that I put into my routes.rb file to make things easier for me. Most of them I got by looking at the Restful Peepcode which I found very worthwhile. Here are those routes in case you're interested:
#easier routes for restful_authentication
map.signup '/signup', :controller => 'users', :action => 'new'
map.login '/login', :controller => 'sessions', :action => 'new'
map.logout '/logout', :controller => 'sessions', :action => 'destroy'
map.activate '/activate/:id', :controller => 'users', :action => 'activate'
map.forgot_password '/forgot_password', :controller => 'users', :action => 'forgot_password'
map.reset_password '/reset_password', :controller => 'users', :action => 'reset_password'
For extra credit, there's also a line in the users_controller.rb create action that will log a registering user into the system before the account is activated. This is sort of a 'good faith' line which assumes that your user is not a spammer.. something I can't do in today's day and age. I commented that line out:
#self.current_user = @user
And that's it! There are a few other things I'm going to do such as add the forgot password functionality. Most of that stuff is really well documented already so I won't go into it here.
Excellent post, very useful indeed - many thanks!
Quick typo: your ‘logout’ named route is defined as map.login (line 3 of under #easier routes for….)
Chris
Fixed thanks!
vince