Rails and oauth-plugin part 1: the provider

These days, I have been playing a lot with Oauth and its RoR implementation, oauth-plugin. Its documentation is a bit short, so here is a tutorial to show how to use it, both in provider and consumer mode. And we will even make them communicate with each other.

We will now build an Oauth provider using oauth-plugin for authorization and Devise for authentication. And we will add a controller protected by Oauth.

Starting up

A few instructions to create the application. You won't need an explanation for this:

[sourcecode language="bash"]
rails new provider
cd provider
[/sourcecode]

Put this in your Gemfile:

[sourcecode language="ruby"]
source 'http://rubygems.org'
gem 'rails', '3.0.7'
gem 'sqlite3'
gem 'devise'
gem "oauth-plugin", ">= 0.4.0.pre1"
[/sourcecode]

And a few more commands:

[sourcecode language="bash"]
bundle install
rails generate devise:install
rails generate devise User
rake db:migrate
rails generate controller welcome index
rm public/index.html
[/sourcecode]

And don't forget 'root :to => "welcome#index"' in config/routes.rb.

Create the provider

[sourcecode language="bash"]
rails generate oauth_provider oauth

rake db:migrate
[/sourcecode]

You could put something else than "oauth" as parameter, but for the moment, the generator has some bugs (it always generate the class OauthController, but with a different name). I'll check more recent versions of the code.

Now, modify config/application.rb and add:

[sourcecode language="ruby"]
require 'oauth/rack/oauth_filter'
config.middleware.use OAuth::Rack::OAuthFilter
[/sourcecode]

Put in app/models/user.rb:

[sourcecode language="ruby"]

has_many :client_applications

has_many :tokens, :class_name=>"OauthToken",:order=>"authorized_at desc",:include=>[:client_application]

[/sourcecode]

Put in app/controllers/oauth_controller.rb:

[sourcecode language="ruby"]

alias :logged_in? :user_signed_in?

alias :login_required :authenticate_user!

[/sourcecode]

and uncomment authenticate_user.

Put in app/controllers/oauth_clients_controller.rb:

[sourcecode language="ruby"]
alias :login_required :authenticate_user!
[/sourcecode]

And now some data

Create a new controller:

[sourcecode language="bash"]

rails generate controller data index

[/sourcecode]

And now, edit your controller:

[sourcecode language="ruby"]
class DataController < ApplicationController
before_filter :oauth_required

def index
@data = { "coincoin" => "o< o<" }

respond_to do |format|
format.json { render :json => @data }
end

end
end
[/sourcecode]

UPDATE

I discovered a few bugs in this tutorial, so here are the fixes.

oauth-plugin needs the function current_user=, so add this to your ApplicationController:

[sourcecode language="ruby"]
def current_user=(user)
current_user = user
end
[/sourcecode]

Next, to handle revocation, you need to add this to config/routes.rb:

[sourcecode language="ruby"]
post 'oauth/revoke'
[/sourcecode]

And at last, you need to fix the rack filter. The current code doesn't verify the token validity, and lets revoked tokens access your data.
You have to modify lib/oauth/rack/oauth_filter.rb in the oauth-plugin gem folder.
Replace the line 46:

[sourcecode language="ruby"]
oauth_token = client_application.tokens.first(:conditions=>{:token => request_proxy.token})
[/sourcecode]

by

[sourcecode language="ruby"]
oauth_token = ClientApplication.find_token(request_proxy.token)
[/sourcecode]

And that's it!

You now have a working provider. OauthController handles all the communication with the consumers. OauthClientsController manages the registration of new consumers. They both have customizable views: oauth for the authorization part (for users) and oauth clients for the consumers. And you just need the oauth_required filter to manage access to your data.

And now, you can go to /users/sign_up, then /users/sign_in, then /oauth_clients to register a new client application. You just need to give a name for your application, your URL, and a callback URL.

In the next post, we will build a consumer, and this consumer will access the provider's data.