HTTP Basic Authentication using restful_authentication with Rails 1.2

Posted by Ben on September 27, 2007

I ran into a small issue this morning with the restful_authentication plugin. We are developing an application where we need to provide a JSON data feed. The feed is protected using HTTP Basic Authentication. There is currently a bug in the plugin which makes Basic Authentication fail. When using the following curl command, I kept receiving a 406 http code from the server.

curl -X GET --basic -u bcurren:test http://localhost:3000/accounts.js

The reason for the 406 error was two fold: 1. there was a bug in restful_authentication which rejects all username and password combinations, even if they are valid 2. restful_authentication does not respond to js requests, so a 406 error is returned, meaning the request type is not valid.

To fix the bug in restful_authentication, I changed the following:

def login_required
  self.current_user ||= User.authenticate(username, passwd) || :false if username && passwd
  logged_in? && authorized? ? true : access_denied
end

to the following

def login_required
  if self.current_user == :false && username && passwd
    self.current_user = User.authenticate(username, passwd) || :false
  end
  logged_in? && authorized? ? true : access_denied
end

The bug is a result of :false being returned from current_user rather than false or nil. Since :false is a symbol it always evaluates to true when used in conditions. The updated code explicitly test if the current_user == :false

After the bug fix above, I was able to successfully retrieve the JSON feed when supplying a valid username and password. However, when submitting and invalid username and password, the application was return a 406 code rather than a 401. This does not result from a bug in restful_authentication but from a lack of support for js requests out of the box. I simply added the following to the access_deniedmethod in authentication_system.rb.

  accepts.js do
    headers["Status"]           = "Unauthorized"
    headers["WWW-Authenticate"] = %(Basic realm="Web Password")
    render :text => "Could't authenticate you", :status => '401 Unauthorized'
  end

I hope this post helps anyone who is tackling the similar issue in Rails 1.2. I will post about some of the other trail and tribulations of using the to_json method in Rails 1.2 in the near future.