Easy Rails API Authentication Using restful-authentication

You’ve got a Rails application and you’d like to add an API. This tutorial assumes you are using restful-authentication.

Creating the API key

You’ll need to add a new column, api_key, to your users table. This field will store the unique API key used for authentication.

./script/generate migration AddApiKeyToUsers
class AddApiKeyToUsers < ActiveRecord::Migration
  def self.up
    add_column :users, :api_key, :string, :limit => 40, :default => ""
  end
 
  def self.down
    remove_column :users, :api_key
  end
end

The API key is a simple SHA hash based on the current time and a random number — we need this to be unique. We’re not going to enable the API for a user by default, though you could easily do this using before_create. Instead, we’ll let the user enable or disable the API using the API Keys Controller, which we’ll create next.

Make the following changes to your app/models/user.rb file:

class User < ActiveRecord::Base
 
  def enable_api!
    self.generate_api_key!
  end
 
  def disable_api!
    self.update_attribute(:api_key, "")
  end
 
  def api_is_enabled?
    !self.api_key.empty?
  end
 
  protected
 
    def secure_digest(*args)
      Digest::SHA1.hexdigest(args.flatten.join('--'))
    end
 
    def generate_api_key!
      self.update_attribute(:api_key, secure_digest(Time.now, (1..10).map{ rand.to_s }))
    end
 
end

Now, let’s create the API Keys Controller, which we’ll use to allow the user to create, re-generate, and delete their API Key. This effectively allows the user to enable and disable API access to their account.

./script/generate controller APIKeys
class APIKeysController < ApplicationController
  before_filter :login_from_cookie
  before_filter :login_required
 
  # Create or re-generate the API key
  def create
    current_user.enable_api!
 
    respond_to do |format|
      format.html { redirect_to edit_user_path(current_user) }
    end
  end
 
  # Delete the API key
  def destroy
    current_user.disable_api!
 
    respond_to do |format|
      format.html { redirect_to edit_user_path(current_user) }
    end
  end
 
end

Add the resource to config/routes.rb

map.resource :api_key

You’ll need to allow the user to enable and disable the API, as well as re-generate their API key if they feel it’s been compromised. Add something like this to app/views/users/edit.html.erb:

<% if @user.api_is_enabled? %>
  <p>
    Your API Key: 
    (<%= link_to "re-generate", api_key_path, :method => :post %> | <%= link_to "disable", api_key_path, :method => :delete %>)
  </p>
  <p>
    <strong><%= @user.api_key %></strong>
  </p>
<% else %>
  <p>	
    You'll need a unique key to make calls to the API.  Remember to keep this key a secret as it can be used to access your account.
  </p>
  <p>
    <%= link_to("Get a key", api_key_path, :method => :post) %>
  </p>
<% end %>

Authenticating the API Key

Add the following to lib/authenticated_system.rb:

def login_from_api_key
  self.current_user = User.find_by_api_key(params[:api_key])
end

Modify lib/authenticated_system.rb to add login_from_api_key as follows:

def current_user
  @current_user ||= (login_from_session || login_from_api_key || login_from_basic_auth || login_from_cookie) unless @current_user == false
end

Optionally, if you’d also like to support access to the API via HTTP Basic Authentication using the API key in addition to a user’s login and password, you can make the following change to app/models/user.rb:

def self.authenticate(login, password)
  return nil if login.blank? || password.blank?
  if password.downcase == "x"
    # This is an API request
    u = find_by_api_key(login)
  else
    u = find_by_login(login.downcase)
    u && u.authenticated?(password) ? u : nil
  end
end

When prompted to log in using HTTP Basic Authentication, use the API Key as the login and ‘X’ as the password.

Testing it Out

Assuming you have a RESTful resource called Items, you should be able to use curl as so:

curl http://www.yoursite.com/items/1.xml?api_key=356a192b
Saturday, May 16th, 2009 by Justin Programming 1 Comment

God Init.d Script for CentOS

This is the /etc/init.d/god script we are using for God on CentOS 5.

Get God

sudo gem install god

Create a Default Config File

sudo touch /etc/god.conf

Create the Init Script

Put this in /etc/init.d/god

#!/bin/bash
#
# God
#
# chkconfig: - 85 15
# description: start, stop, restart God
#              
 
RETVAL=0
 
case "$1" in
    start)
      /usr/local/bin/god -P /var/run/god.pid -l /var/log/god.log
      /usr/local/bin/god load /etc/god.conf
      RETVAL=$?
  ;;
    stop)
      kill `cat /var/run/god.pid`
      RETVAL=$?
  ;;
    restart)
      kill `cat /var/run/god.pid`
      /usr/local/bin/god -P /var/run/god.pid -l /var/log/god.log
      /usr/local/bin/god load /etc/god.conf
      RETVAL=$?
  ;;
    status)
      RETVAL=$?
  ;;
    *)
      echo "Usage: god {start|stop|restart|status}"
      exit 1
  ;;
esac      
 
exit $RETVAL

Make it Executable

sudo chmod a+x /etc/init.d/god

Ensure God Launches on System Boot

sudo chkconfig --add god
sudo chkconfig --level 345 god on

Fire it Up

sudo /etc/init.d/god start
Friday, May 15th, 2009 by Justin Programming No Comments

Private, Authenticated RSS Feeds in Rails

So, you want to allow your users to access a RSS feed of private data in their account.  Something like,

http://subdomain.yoursite.com/comments/123abc/feed.rss, where “123abc” is a secret token.

Here’s how to do this in Rails 2.1.2, though it will most likely work in other versions of Rails.  In this example, each Account has_many Comments, and we’d like to get a RSS feed of comments.

1. Create a route for the feed in your routes.rb file.

map.resources :accounts do |account|
  account.comments_feed 'comments/:token/feed.:format', :controller => 'comments', :action => 'feed', :token => nil
end

2. Create a new migration and add the feed_token to the Account.

class AddFeedTokenToAccount < ActiveRecord::Migration
  def self.up
    add_column :accounts, :feed_token, :string, :limit => 40, :default => ""
  end
 
  def self.down
    remove_column :accounts, :feed_token
  end
end

3. When a new Account is created, create the feed_token. We’ll just use a standard SHA1 hash of the current time and the account id. This should be unique enough. Let’s also create a method to validate the feed token. All of this goes in app/models/account.rb

class Account < ActiveRecord::Base
  before_create :create_feed_token
 
  def valid_feed_token?(token)
    self.feed_token == token
  end
 
  protected
 
  def create_feed_token
    self.feed_token = Digest::SHA1.hexdigest(Time.now.to_s + self.id.to_s)
  end
end

4. Add a new feed method to your Comments controller. This goes in app/controllers/comments_controller.rb

def feed
  @account = Account.find(params[account_id])
  @comments = @account.comments
  @token = params[:token]
 
  respond_to do |format|
    if @account.valid_feed_token?(@token)
      format.rss { render :layout > false }
    else
      format.rss { render :nothing > true, :status > :forbidden }
    end
  end
end

5. Create the file app/views/comments/feed.rss.builder. This is what generates the RSS feed.

xml.instruct! :xml, :version => "1.0"
xml.rss :version => "2.0" do
  xml.channel do
    xml.title "Comments"
    xml.description "A bunch of comments"
    xml.link account_comments_url(@account)
 
    for comment in @comments
      xml.item do
        xml.title comment.title
        xml.description comment.body
        xml.pubDate comment.created_at.to_s(:rfc822)
        xml.link account_comments_url(@account)
      end
    end
  end
end

6. Link to the comments RSS feed somewhere on your site. For example, somewhere in app/views/comments/index.html.erb place the following link:

<%= link_to 'Subscribe', account_comments_feed_path(@account, :format => :rss, :token => @account.feed_token)) %>
Tuesday, February 17th, 2009 by Justin Programming 2 Comments

A Private Web Beta in Seconds with Prefinery

Don’t write it yourself

You’ve got some slick new Web site and you’d like to run a private beta.  Maybe this is because you’re convinced your site is so awesome you’re going to get more traffic than your ready for.  Or, maybe you’d just like to work out the bugs with a small, select group of users.  Either way, you need a process for collecting the e-mail addresses for people who are interested in signing up, generating unique invitation codes, and sending an e-mail invitation.

This process is a pain in the ass, and not something core to your awesome new web site.  Heck, you’ll probably only be in private beta for a few months so you’ll end up throwing away all the code you write to manage the private beta.

Prefinery can manage this entire process for you.  Here’s how it works …

1.  Sign up for a free Prefinery account

You specify what contact information (name, email, address, phone, etc.) must be gathered when users request an invitation code.

2.  Install the invitation widget

This is as easy as copying and pasting a few lines of Javascript.

3.  Users visit your web site and request an invitation code

The Prefinery invitation widget gets launched when a user clicks a link or image on your site.  Your users never leave your web site!

4.  Poof, an invitation code is generated

You log into Prefinery where you see the thousands of people who are begging to use your awesome new site.  Approving access is as easy as clicking a button.  Once access is approved, an e-mail (from your address, of course) is sent to the user.

5.  Users sign up on your site

We give you the secret formula so that you can decode a Prefinery invitation code when users sign up on your site. We provide code samples in various languages, including Ruby, PHP, and Python.  In most cases this process is as simple as one line of source code.

Learn more

You can learn more about Prefinery by taking the tour, checking out the screenshots, or signing up for a free account.

Monday, February 16th, 2009 by Justin Prefinery No Comments

Backup Your WordPress Blog to Amazon S3 using Ruby

I’ve been running this blog for months without a backup solution.  Mostly because I didn’t really care for any of the existing solutions which required me to do too many manual steps.  I wanted something completely hands-off (automatically run every night), secure and reliable (store the backups on Amazon’s S3).

So, enjoy wordpress-s3-backup.  Use it to backup your WordPress blog — both the database and the site.  It’s a Ruby Rake script and you’ll need the AWS-S3 gem.  Stick it in your crontab to run nightly and move on with your life.

Get it here.

Thursday, January 15th, 2009 by Justin Programming 1 Comment
 
Located in beautifully weird Austin, Texas, Compulsivo has been obsessing over writing simple Web software using Ruby on Rails for over two years. Learn more
Prefinery: Simple, online beta management software'

Launch a private beta for your Web application in minutes. Prefinery takes care of collecting e-mail addresses, generating invitation codes, and sending invitations for your private beta. Your customers never leave your site, and e-mail invitations are sent from your address.