CSC/ECE 517 Fall 2013/ch1 1w19 rj

From Expertiza_Wiki
Revision as of 00:42, 17 September 2013 by Rganesh (talk | contribs) (→‎The Workflow)
Jump to navigation Jump to search

Using secure API authorization via OAuth

OAuth is the de facto standard authentication mechanism used by prominent websites like Facebook and Twitter. This wiki discusses Ruby support for OAuth and highlight using examples.

Introduction to OAuth

What is OAuth?

The OAuth protocol was originally created by a small community of web developers from a variety of websites and other Internet services who wanted to solve the common problem of enabling delegated access to protected resources.(1) The Web Authorization (OAuth) protocol allows a user to grant a third-party Web site or application access to the user's protected resources, without necessarily revealing their long-term credentials, or even their identity. For example, a photo-sharing site that supports OAuth could allow its users to use a third-party printing Web site to print their private pictures, without allowing the printing site to gain full control of the user's account and without having the user share his or her photo-sharing sites' long-term credential with the printing site.(2) An area where OAuth is more evolved than some of the other protocols and services is its direct handling of non-website services. OAuth has built-in support for desktop applications, mobile devices, set-top boxes, and of course websites. (2)

Terminologis:

  • Client: An HTTP client capable of making OAuth- authenticated requests
  • Server: An HTTP server capable of accepting OAuth- authenticated requests.
  • Protected resource: An access-restricted resource that can be obtained from the server using an OAuth-authenticated request.
  • Resource owner: An entity capable of accessing and controlling protected resources by using credentials to authenticate with the server.
  • Credentials: Credentials are a pair of a unique identifier and a matching shared secret.OAuth defines three classes of credentials: *Client, temporary, and token, used to identify and authenticate the client making the request, the authorization request, and the *access grant, respectively.
  • Token: A unique identifier issued by the server and used by the client to associate authenticated requests with the resource owner whose authorization is requested or has been obtained by the client. Tokens have a matching shared-secret that is used by the client to establish its ownership of the token, and its authority to represent the resource owner.(1)

How OAuth operates?

In the traditional client-server authentication model, the client uses its credentials to access its resources hosted by the server. OAuth introduces a third role to this model: the resource owner. In the OAuth model, the client (which is not the resource owner, but is acting on its behalf) requests access to resources controlled by the resource owner, but hosted by the server. In order for the client to access resources, it first has to obtain permission from the resource owner. This permission is expressed in the form of a token and matching shared-secret. The purpose of the token is to make it unnecessary for the resource owner to share its credentials with the client. Unlike the resource owner credentials, tokens can be issued with a restricted scope and limited lifetime and revoked independently. This specification consists of two parts. The first part defines a redirection-based user-agent process for end-users to authorize client access to their resources, by authenticating directly with the server and provisioning tokens to the client for use with the authentication method. The second part defines a method for making authenticated HTTP requests using two sets of credentials, one identifying the client making the request, and a second identifying the resource owner on whose behalf the request is being made.(2)

The Workflow

File:Https://upload.wikimedia.org/wikipedia/commons/1/1d/Wikipage.jpg

OAuth uses tokens to represent the authorization granted to the client by the resource owner. Typically, token credentials are issued by the server at the resource owner's request, after authenticating the resource owner's identity (usually using a username and password). There are many ways in which a server can facilitate the provisioning of token credentials. This section defines one such way, using HTTP redirections and the resource owner's user-agent. This redirection-based authorization method includes three steps:

  1. The client obtains a set of temporary credentials from the server (in the form of an identifier and shared-secret). The temporary credentials are used to identify the access request throughout the authorization process.
  2. The resource owner authorizes the server to grant the client's access request (identified by the temporary credentials).
  3. The client uses the temporary credentials to request a set of token credentials from the server, which will enable it to access the resource owner's protected resources.

The server MUST revoke the temporary credentials after being used once to obtain the token credentials. It is RECOMMENDED that the temporary credentials have a limited lifetime. Servers SHOULD enable resource owners to revoke token credentials after they have been issued to clients. (1)

OAuth in Ruby

Ruby has several gems for turning your web application into an OAuth Provider or OAuth Consumer. The most popular, of course, is oauth. However, this is NOT a Rails plugin, but could easily be used for the foundation for such a Rails plugin. As a matter of fact it has been pulled out from an oauth-plugin which now requires this GEM [Source]. Some documentation on the oauth gem is available at it's canonical GitHub repository.

OAuth Provider support

The oauth-plugin can generate an OAuth Provider that supports the following out of the box:

  • User can register their own applications to receive consumer key/secret pairs.
  • Provider supports standard best practises out of the box hmac-sha1, etc.
  • Users can manage and revoke tokens issued in their name
  • Easy before filter to provide OAuth protection on your actions

OAuth Consumer support

The oauth-plugin can generate an OAuth Consumer which includes a controller to manage the authentication flow between your application and any number of external OAuth secured applications that you wish to connect to.

Creating a sample OAuth Application

TODO: Add Ruby/Rails version used, Additional Gems used

Creating an OAuth Provider

Following steps will create a basic Rails application that uses OAuth to authenticate the requests.
1. Create a Rails application and remove public/index.html

rails new OAuthProviderApp


2. Add devise, oauth-plugin and dynamic_form gems to your Gemfile

gem 'dynamic_form'
gem 'devise'
gem 'oauth-plugin'


3. Run bundle install to install the Gems

bundle install


4. Run the devise:install and devise User generators to generate the User model, controller and views

rails generate devise:install
rails generate devise User

The above commands generates the migration and model for User.

5. Run the oauth_provider generator

rails generate oauth_provider

This will generate the migrations, models, controllers, views and routes for the following:

  • OAuthToken or AccessToken - The token used to associate the request with the resource owner.
  • ClientApplication - Client application that needs access to the services offered by the Server on behalf of the Resource owner
  • OAuthNonce - Used for verifying requests from the client


6. Migrate the database to create tables for User, OAuthToken, ClientApplication, and OAuthNonce in the database

rake db:migrate


7. To test the application, add the following route to your config/routes.rb

root :to => 'oauth_clients#index'

Thus, the home page will display the list of OAuth clients registered by a user (developer of OAuth client).

8. Add ClientApplication and OAuthToken associations to User model (app/models/user.rb)

has_many :client_applications
has_many :tokens, :class_name => 'Oauth2Token', :order => 'authorized_at desc', :include=>[:client_application]


9. Add the following accessors to the corresponding models

attr_accessor :expires_at                                                       # Add this to OauthToken
attr_accessible :user, :client_application                                      # Add this to AccessToken
attr_accessible :nonce, :password, :timestamp                                   # Add this to OauthNonce
attr_accessible :callback_url, :client_application                              # Add this to RequestToken
attr_accessible :user, :client_application, :scope                              # Add this to Oauth2Token
attr_accessible :client_application, :user, :scope, :callback_url               # Add this to Oauth2Verifier
attr_accessible :name, :url, :callback_url, :support_url, :token_callback_url   # Add this to ClientApplication

This is required since the generator for oauth_provider is outdated.

10. Add the following alias to app/controllers/oauth_controller.rb and app/controllers/oauth_clients_controller.rb after the class declaration

alias :login_required :authenticate_user!

This is required because oauth-plugin uses login_required method to determine whether the user is authenticated or not. In order to determine whether the user is authenticated or not, we use the authenticate_user! method provided by devise gem.

11. Add the following filter to config/application.rb

require 'oauth/rack/oauth_filter'               #Add before declaration of module OAuthProviderApp
config.middleware.use OAuth::Rack::OAuthFilter  #Add after declaration of class Application

This adds the OAuthFilter to the middleware layer and thus allows filtering out unauthorized calls.

12. Add the following method to app/controllers/application_controller.rb

def current_user=(user)
  current_user = user
end

This method is required by oauth-plugin to let devise know who the user is.

13. Generate a new controller that will define the services provided by the this provider

rails generate controller API::V1::Data


14. Add a dummy service method to this controller (app/controllers/api/v1/data_controller.rb) and have it render some response. This will be the service that the OAuth client will try to access.

class Api::V1::DataController < ApplicationController
  respond_to :json, :xml
  oauthenticate :interactive=>false                # Actions in this controller can only be accessed via OAuth
  def show                                         # This is the service OAuth client will call
    respond_with 'My birthday is on 09/05/2013'
  end
end


15. Add the following to config/routes.rb

namespace :api do
  namespace :v1 do
    match "show" => "data#show"
  end
end


16. Run the Rails server

rails server -p 3000


17. Sign up as a client developer for the OAuthProviderApp at http://localhost:3000/users/sign_up

Client registration



Client registration success



18. Register your application with the following parameters and Note down the Consumer key and Consumer secret

Name:                 test
Main Application URL: http://localhost:4000/
Callback URL:         http://localhost:4000/oauth_consumers/test/callback

In the next section, we will create an OAuthConsumerApp with these parameters. OAuthConsumerApp will act as a client for OAuthProviderApp.

Application registration



Application registration success



Creating an OAuth Consumer

Following steps will create a basic Rails application that uses OAuth to access customer's data (birth date) from an OAuth provider.
1. Create a Rails application and remove public/index.html

rails new OAuthConsumerApp


2. Add devise and oauth-plugin gems to your Gemfile

gem 'devise'
gem 'oauth-plugin'


3. Run bundle install to install the Gems

bundle install


4. Run the devise:install and devise User generators to generate the User model, controller and views

rails generate devise:install
rails generate devise User

The above commands generates the migration and model for User.

5. Run the oauth_consumer generator

rails generate oauth_consumer user

This will generate the migrations, models, controllers and routes for ConsumerToken which is used by this Consumer to authenticate with the Provider.

6. Migrate the database to create tables for User and ConsumerToken

rake db:migrate


7. Add the following alias to app/controllers/oauth_consumers_controller.rb after the class declaration

alias :login_required :authenticate_user!

This is required because oauth-plugin uses login_required method to determine whether the user is authenticated or not. In order to determine whether the user is authenticated or not, we use the authenticate_user! method provided by devise gem.

8. Add the following methods to app/controllers/application_controller.rb

def current_user=(user)
  current_user = user
end
def logged_in?
  user_signed_in?
end

This method is required by oauth-plugin to let devise know who the user is and whether (s)he is logged in or not.

9. Create a new model that subclasses ConsumerToken for the above OAuthProvider service

class TestToken < ConsumerToken
  TEST_SETTINGS={
      :site => "http://localhost:3000",
      :request_token_path => "/oauth/request_token",
      :access_token_path => "/oauth/access_token",
      :authorize_path => "/oauth/authorize"
  }
  def self.consumer(options={})
    @consumer ||= OAuth::Consumer.new(credentials[:key], credentials[:secret], TEST_SETTINGS.merge(options))
  end
end

The purpose of this subclass is to define the consumer method that is based on the Provider.

11. Add an association to this token to User model

has_one :test, :class_name=>"TestToken", :dependent=>:destroy


10. Add the following to config/initializers/oauth_consumers.rb defining the Consumer key and secret provided by the OAuthProviderApp

OAUTH_CREDENTIALS={
   :test => {
       :key => "CRcIJ15MwSqlDTxsH8MpO3En4wjaOxkqeofLioH4",      # From Step 18 of previous section
       :secret => "C7uci8xkyMShCf4SNXWPclKbBo3ml1Zf2W2XWu4W",   # From Step 18 of previous section
       :expose => true
   }
}


11. Generate a controller for testing the application

rails generate controller welcome index


12. Add logic to a controller action that performs the OAuth workflow as discussed in Introduction

class WelcomeController < ApplicationController
  def index
    if(current_user)
      @consumer_tokens = TestToken.all :conditions => {:user_id => current_user.id}
      if(@consumer_tokens.first)
        @token = @consumer_tokens.first.client
        render inline: @token.get("/api/v1/show").body
      else
        redirect_to '/oauth_consumers/test'
      end
    else
      redirect_to new_user_registration_path
    end
  end
end


13. Add the following route to config/routes.rb

root :to => 'welcome#index'


14. Experience the OAuth workflow

A customer (who has an account with OAuthProviderApp) registers with OAuthConsumerApp



Upon registration, the customer is redirected to OAuthProviderApp to import data from OAuthProviderApp to OAuthConsumerApp



Customer authorizes OAuthConsumerApp to access his/her data from OAuthProviderApp



OAuthConsumerApp can now access Customer's data from OAuthProviderApp