wiki:Examples/Authentication

Authentication

HTTP Basic Authentication in Rails

Many services you will access are going to require authentication of some sort. We can quickly add basic authentication to our rails application from Serve It Up by making app/controllers/application_controller.rb look like this:

class ApplicationController < ActionController::Base
  helper :all # include all helpers, all the time
  protect_from_forgery # See ActionController::RequestForgeryProtection for details

  before_filter :authenticate

  private
    def authenticate
      authenticate_or_request_with_http_basic do |id, password|
        id == "Ace" && password == "newenglandclamchowder"
      end
    end
end

Of course you would never hard code user id's and passwords in a real application, and you would always salt and hash your passwords before storing them in your database right? If you thought about breakfast when I said salt and hash then I encourage you to do a little research. There's a decent article on  Password cracking on Wikipedia to get you started. If you decide to use MD5 to hash your passwords, you might as well just post them on facebook.

There are  lots of plugins and stuff for rails that make this easier. Did I mention that only idiots store plaintext passwords? Please use a strong hash for password storage, the best one out there is  bcrypt, designed by Niels Provos and David Mazieres of the OpenBSD team. It's a special purpose user password scheme implemented by Smart People. There is of course a ruby implementation of  bcrypt. In Rails 3.1, you get  password encryption baked into ActiveModel (using bcrypt of course).

Of course you would also never use HTTP Basic authentication without also using TLS or SSL, because HTTP Basic sends the username and password in the clear.

Security rant done.

HTTP Basic Authentication in jactiveresource

With these changes made to the rails service, jactiveresource will now start throwing UnauthorizedAccess exceptions when it tries to access it. So let's provide some credentials to jactiveresource.

ResourceConnection c = new ResourceConnection("http://localhost:3000");
c.setUsername("Ace");
c.setPassword("newenglandclamchowder");

You might be tempted to do something like:

ResourceConnection c = new ResourceConnection("http://Ace:newenglandclamchowder@localhost:3000");

The problem with embedding these into the URI is that if your password is "h/f9#:b2" then your in  barney. So don't do it: if you do, the credentials will be ignored.

Other kinds of authentication in jactiveresource

Say your rails app has a mechanism for authentication that relies on HTTP Cookies. For example, your Rails app might expect a cookie to be set with a particular value, and it then grants access to the resource instead of using HTTP Basic authentication. Once you have acquired the cookie (jactiveresource doesn't help with that), you need a way to get your ResourceConnection object to use it. To do so, you need to create your own HttpClientFactory.

A HttpClientFactory is the thing that a ResourceConnection calls to get HttpClient objects, which it then uses to talk to your rails app. You can either create a new class that implements the AbstractHttpClientFactory interface, or you can subclass the default factory. For this example, we will choose the subclass method. So we create a new class:

import java.net.URL;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.DefaultHttpClient;

public class CookieFactory extends DefaultHttpClientFactory {
  private BasicCookieStore bcs;

  public void setCookieStore(BasicCookieStore bcs) {
    this.bcs = bcs;
  }

  public BasicCookieStore getCookieStore() {
    return this.bcs;
  }

  @Override
  public DefaultHttpClient getHttpClient(URL site) {
    DefaultHttpClient c = super.getHttpClient(site);
    c.setCookieStore(bcs);
    return c;
  }
}

And magically get the cookie (you figure that part out), and tell the resource connection to use the cookie factory to make HttpClient? objects:

BasicCookieStore myCookieStore = new BasicCookieStore();
// here invoke magic to get the cookie and add it to the cookie store
CookieFactory f = new CookieFactory();
f.setCookieStore(myCookieStore);
ResourceConnection c = new ResourceConnection("http://localhost:3000");
c.setHttpClientFactory(f);