Skip to content

Build Mobile Apps in the Cloud with Omniauth, Httparty and Force.com

quintonwall edited this page Mar 23, 2011 · 2 revisions

##Introduction

OAuth2 is becoming the defacto standard for web2.0 authentication. For many of us mere mortals, the complexity of how OAuth2 works is a steep learning curve, especially when many authentication providers require, or respond with additional parameters beyond what the OAuth2 specification defines. Thankfully, for Ruby, Omniauth comes to the rescue!

Omniauth is an open source project which provides support for many of the main providers out there: twitter, facebook, foursquare, and many more. But what happens if your provider is not one of those supported by Omniauth? Never fear, Omniauth is designed in such a way that it is very easy to implement custom strategies---interchangable logic which encapsulates the steps required to successfully authenticate with an Oauth2 provider.

For our example, we want to connect to the Force.com platform to retrieve a list of records via the platform’s REST API. This is a pretty typical use case for building mobile apps. Force.com is not one of the out of the box authentication providers omniauth supports, so we need to write our own.

Before we jump into writing Force.com Strategy let’s have a quick look at how Omniauth works.

##How Omniauth works

Omniauth relies on the request and callback sequence as defined in OAuth2 specification. If you want more a more in-depth discussion of the OAuth dance check out the Digging Deeper into OAuth2 at Salesforce.com article for a great write up. As I said at the start of the article, Omniauth extracts away a lot of the complexity of working with OAuth, so we can skip all of that information and focus on just what we need to. Nice!

The diagram below depicts how Omniauth handles the request and callback sequence:

The request phase of the OAuth dance typically redirects to a providers website which prompts the user to enter their credentials with the provider calling back to Omniauth with a success or failure message.

Since we are going to write our own custom strategy for Omniauth, we want to dig a little deeper into how Omniauth works.

You will see in the diagram above that Omniauth is broken into two parts: the core, and the strategies. The core library handles much of the heavy lifting by incepting request and callback URLs, marshing standard OAuth aspects such as tokens and credentials, as well as determining which strategy to use. The strategy contains provider specific implementations and allows you to inject customizations at particular points in the OAuth lifecycle, most notibly the request and callback mentioned above. We are going to write our own custom strategy to connect to the Force.com platform.

##The Force.com Strategy

With most of the heavy lifting done for us by Omniauth, we can focus on the specific provider implementation details for our strategy. The Force.com platform follows the OAuth2 spec pretty closely which makes our job quite easy. First we need to initialize our strategy with our specific end points:

require 'omniauth/oauth'
require 'multi_json'

# Omniauth strategy for using oauth2 and force.com 
# Author: [email protected]
#
module OmniAuth
  module Strategies
    class Forcedotcom < OAuth2
     
      def initialize(app, client_id = nil, client_secret = nil, options = {}, &block)
        client_options = {
          :site => "https://login.salesforce.com",
          :authorize_path      => "/services/oauth2/authorize",
          :access_token_path   => "/services/oauth2/token"
        }
        super(app, :forcedotcom, client_id, client_secret, client_options, &block) 
      end

   end
 end
end

The next part of the OAuth dance is the request. Let’s add a hook for that in our strategy setting an additional request parameter required by Force.com:

 def request_phase
        options[:response_type] ||= 'code'
        super
 end

Next comes the callback phase. Time for another hook and an additional request parameter:

def callback_phase
        options[:grant_type] ||= 'authorization_code'
        super
end

Lastly, as part of a successful authentication, Force.com returns an additional parameter, instance_url, which we will use to make REST calls with later in the article. Omniauth strategies provide us with a convenient hook to handle additional parameters returned in the payload:

def auth_hash
          OmniAuth::Utils.deep_merge(super, {
            'instance_url' => @access_token['instance_url']
          })
 end

That’s it. We just implemented our custom strategy for Force.com. Save the complete code in a file called forcedotcom.rb and drop it in your Rails 3 applications lib directory (or get it from Github here)

##Setting up your Remote Access Provider Now it is time to start using our strategy. Unlike other providers like Twitter or Facebook, the Force.com platform allows you to set up your own Remote Access providers. Here is where you need to know a little about Force.com, and have a Developer Edition environment set up. If you don’t, you can sign up for a free account here. One of the great things about the Force.com platform is you can quickly create your own Remote Access Provider. This is super helpful if you are creating mobile apps for example and want to centralize the authentication and data access getting it all from the cloud.

Once you have your Developer Edition environment, and have logged in, jump back to the Digging Deeper into OAuth2 at Salesforce.com article and follow the steps described in the section titled “Configuring OAuth2.0 access in your Application”. Use the following URL for the Callback URL. (We will explain more about the callback URL soon).

https://localhost:3000/auth/forcedotcom/callback

With our Remote Access provider set up, it is time to put our strategy to work.

##Using our custom strategy in Rails 3 Go ahead and create a Rails 3 app. I called mine “omniauth-demo”

$ rails new omniauth-demo

Once you have created your app, drop our forcedotcom.rb custom strategy in the lib directory of our app.

Now is a good time to make sure you have the Omniauth .2.x gem installed. If you don’t install it now:

$ sudo gem install omniauth

Go ahead and add the requires reference to our Gemfile. We will also be using another gem, httparty to work with our REST calls soon. You may as well make sure you have that gem installed now, and add it to the Gemfile while you are here:

gem 'omniauth', '0.2.0'
gem 'httparty'

Ok, just a few more config steps and we are done. I assure you, this is MUCH easier than writing your own custom OAuth2 implementation.

Next, we need to tell Rails to load our custom strategy. We can do this by adding a file, omniauth.rb to the apps config/initializers directory with the following snippet of code:

require 'forcedotcom'

# Set the default hostname for omniauth to send callbacks to.
# seems to be a bug in omniauth that it drops the httpS
# this still exists in 0.2.0
OmniAuth.config.full_host = 'https://localhost:3000'

module OmniAuth
  module Strategies
    #tell omniauth to load our strategy
    autoload :Forcedotcom, 'lib/forcedotcom'
  end
end


Rails.application.config.middleware.use OmniAuth::Builder do
  provider :forcedotcom, 'consumer-key', 'consumer-secret'
end

You will notice two values, consumer-key, and consumer-secret. Substitute the values provided for you when you created your Remote Access provider in the section above.

##Handling the callback Our app setup is almost complete. The last thing we need to do is generate a controller to handle the callback and a route.

$ rails generate controller sessions create

Next, add the following to your config/routes.rb file:

#add our oauth redirect route - qw
  match '/auth/:provider/callback', :to => 'sessions#create'
  match '/auth/failure', :to => 'sessions#fail'

Within our Sessions Controller, let’s update our create method to output the response of our oauth request.

class SessionsController < ApplicationController
  def create
    ENV['sfdc_token'] = request.env['omniauth.auth']['credentials']['token']
    ENV['sfdc_instance_url'] = request.env['omniauth.auth']['instance_url']
    render :text => request.env['omniauth.auth'].inspect
  end
end

##Testing our strategy. We now have everything we need to successfully authenticate against Force.com using our custom strategy. The only remaining thing we need to do is enable our local server to support SSL. Earlier in this article we set our callback URL to https://localhost:3000/auth/forcedotcom/callback. Now that we have configured our strategy, you will see that the strategies name, in our case forcedotcom, is part of the callback URL. If you were using a different strategy, the provider name would be different too. You will also see that we set our callback url to be https. The Force.com platform requires that all callback URL’s uses https.

I am using the standard WEBrick server provided with Rails and followed the steps described in this article to configure my server to support https. The sample app I have provided already has the appropriate additions to the script/rails file and points to lib/certs for your self-signed certificates. All you need to do is generate your own certificates. I used the java keytool , or you could use something like openssl if you prefer.

Once you can start your server using https, we can continue.

Now that your server supports https, start it up, and type in the following url in your browser:

https://localhost:3000/auth/forcedotcom

Omniauth will incept this request, and hand over control to our custom strategy which should redirect you to authenticate against Force.com. Use your username and password to login. At this stage you should be presented with a page which allows you to approve the provider request. Once you click approve, Force.com will redirect you back to your callback url which, again will be intercepted by Omniauth and our custom provider, finally sending us to our sessions#create method.

All going well, you should now see the json payload of your successful authentication request, and we did it with very little code!

##Adding a little REST magic A good programmer never rests though (poor pun intended). Authentication is only useful if we can retrieve information as a result. Here is where the Force.com platform helps us out a lot. As part of the signup for a Force.com account, you are automatically provided with a scalable, secure cloud database which you can use REST to access data from. Let’s update our app to pull the first one hundred Accounts in our database.

We are going to use Httparty to make our job really easy. Add the following code into a class called accounts, and save it in your app’s lib directory:

require 'rubygems'
require 'httparty'

class Accounts
  include HTTParty
  #doesn't seem to pick up env variable correctly if I set it here
  #headers 'Authorization' => "OAuth #{ENV['sfdc_token']}"
  format :json
  #debug_output $stderr

  def self.set_headers
    headers 'Authorization' => "OAuth #{ENV['sfdc_token']}"
  end

  def self.root_url
    @root_url = ENV['sfdc_instance_url']+"/services/data/v"+ENV['sfdc_api_version']
  end

  def self.get_first_hundred
    Accounts.set_headers
    get(Accounts.root_url+"/query/?q=#{CGI::escape('SELECT Name, Id from Account LIMIT 100')}")
  end
 
end

You will notice we are using a few environment variables to make our code a little more re-usable and easy to maintain. One of these environment variables ‘sfdc_api_version’ ensures that as the Force.com API versions change, we can easily maintain our code. Let’s set the sfdc_api_version in our config/environment.rb file as it does not need to change dynamically:

ENV['sfdc_api_version'] = '21.0'

Now, go back to your session#create method, and modify the code the following:

require 'Accounts'

class SessionsController < ApplicationController
  def create
    ENV['sfdc_token'] = request.env['omniauth.auth']['credentials']['token']
    ENV['sfdc_instance_url'] = request.env['omniauth.auth']['instance_url']
    #render :text => request.env['omniauth.auth'].inspect
    render :text => Accounts.get_first_hundred.inspect
  end

The sfdc_instance_url is a good example of working with our custom strategy. We overrode the ‘auth_hash’ method in our strategy to include the instance_url parameter which Force.com passes back as part of a successful OAuth request. The instance url is used to determine which instance of Force.com you are going to make a request against with the REST API.

Save and run your app again. You should now be presented with a json payload containing data from Force.com retrieved via REST.

##Summary Writing a custom Omniauth strategy is much easier than trying to implement the whole OAuth request/callback dance. Once we can OAuth to a provider, Force.com in our instance, we can start building some cool apps quickly because we can focus on exactly what we need it, and not low-level plumbing. Throw in the ability to create your own custom providers, and a scalable cloud database and platform, and you have all the ingredients for successful Cloud2 apps. Go ahead and grab the code from github and start building.

You can use the sample app from Github as your starting point to save additional time. If you use the sample app, all you will need to do is change the consumer-key and consumer-secret in config/initializers/omniauth.rb, and generate your own self-signed certificates.

##Kudos Thanks to Dira for a great getting started article on building custom Omniauth strategies, and for the sequence diagrams I used in this how-to.

##Notes Make sure you follow the steps in generating the 'server.key' and 'server.crt' files. Those are critical in getting the server started. (need to place those files in the 'lib/certs' directory.)

##About me Got questions? Twitter is the best place to get a hold of me these days.

Clone this wiki locally