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:

rails new provider
cd provider

Put this in your Gemfile:

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

And a few more commands:

bundle install
rails generate devise:install
rails generate devise User
rake db:migrate
rails generate controller welcome index
rm public/index.html

And don’t forget ‘root :to => “welcome#index”‘ in config/routes.rb.

Create the provider

rails generate oauth_provider oauth

rake db:migrate

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:

require 'oauth/rack/oauth_filter'
config.middleware.use OAuth::Rack::OAuthFilter

Put in app/models/user.rb:


has_many :client_applications

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

Put in app/controllers/oauth_controller.rb:


alias :logged_in? :user_signed_in?

alias :login_required :authenticate_user!

and uncomment authenticate_user.

Put in app/controllers/oauth_clients_controller.rb:

alias :login_required :authenticate_user!

And now some data

Create a new controller:


rails generate controller data index

And now, edit your controller:

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

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:

def current_user=(user)
  current_user = user
end

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

post 'oauth/revoke'

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:

oauth_token = client_application.tokens.first(:conditions=>{:token => request_proxy.token})

by

oauth_token = ClientApplication.find_token(request_proxy.token)

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.

About these ads

30 thoughts on “Rails and oauth-plugin part 1: the provider

  1. Nice post, just a couple points:

    Shouldn’t the line

    alias :login_required :authenticate_user

    actually be (not the exclamation mark):

    alias :login_required :authenticate_user!

    The line that needs to be edited in lib/oauth/rack/oauth_filter.rb is in the oauth-plugin gem folder, not that of the local app.

    Gazler.

  2. Your “update” saved me hours upon hours on work. When I take control of the world I shall erect a statue in your honour.

  3. Great write-up. For me though, it had to be:

    def current_user=(user)
    sign_in(user)
    end

    Just in case anybody else runs into the same issue with oauth-plugin/devise.

    Ulf

  4. Hi i followed the above steps and when i try to generate controllers as above
    rails generate controller data index

    i am getting the following error

    /home/lenovo-10/Desktop/digital/config/application.rb:4:in `require’: no such file to load — oauth/rake/oauth_filter (LoadError)

    from /home/lenovo-10/Desktop/digital/config/application.rb:4:in `’
    from /home/lenovo-10/.rvm/gems/ruby-1.9.2-p290@rails3/gems/railties-3.1.0/lib/rails/commands.rb:21:in `require’
    from /home/lenovo-10/.rvm/gems/ruby-1.9.2-p290@rails3/gems/railties-3.1.0/lib/rails/commands.rb:21:in `’
    from script/rails:6:in `require’
    from script/rails:6:in `’

    what would be the reason and how can i come out from it.

  5. I followed this tutorial exactly, but when I try to authenticate with my consumer, the provider blows up on OauthController#authorize. Stack trace says 0 for 2 arguments for authenticate_user on line 160 of lib/oauth/controllers/provider_controller.rb. Finding that method is easy, however, finding what’s calling it isn’t. I’ve tried debugging it, but rails seems to jump right over my debugger, and adding a breakpoint won’t even work. I’m sure I’m doing something stupid, but I have no idea what, and I’ve been staring at this for hours. Any help would certainly be appreciated.

    • Hello,

      the wrong function wasn’t there, that’s why your breakpoint didn’t halt the app. There was a typo in the tutorial :(

      In app/controllers/oauth_controller.rb, you must replace “alias :login_required :authenticate_user” by “alias :login_required :authenticate_user!”

      I updated the tutorial. Sorry for the mistake.

      • Argh! Thank you so much! I should have looked closer at that, since some other commenters mentioned problems with that method and it wasn’t really clear which one it was supposed to be.

  6. I have a problem with this tutorial. When it calls ‘data / index’ getting the error: Invalid OAuth Request. When the callback is invoked in a consumer app.
    error:
    ActiveRecord:: RecordInvalid in OAuth consumersController # callback

    Validation failed: User can not be blank

    app/controllers/oauth_consumers_controller.rb:16:in `callback’

    The application does not copy the users.
    I do not know where I made a mistake.
    my gems:
    rails 3.0.7
    devise 1.5.0

    ruby 1.9.2p0

    • I don’t really get what your problem is. Are you expecting that the consumer app create a user from the call to the provider? This tutorial is about getting access to another application from one you’re already using (ex: giving your Linkedin account an access to your twitter account). If you want to log in using Oauth, there’s a much simpler solution: the Omniauth gem.

      • Thank you for your quick response
        I wanted to get something like that.
        One application that contains users. Another application is using it.
        For example, a user logs in to the application provider and has access to other applications through the provider.
        One login -> multiple applications -> different roles in applications.

        I use ‘Devise’ and for this I need a solution. I have not found how to create an provider application in the ‘Omniauth’.

        I hoped that OAuth is what I’m looking for

      • That is not really the definition of a provider for Oauth.
        Oauth is an authorization protocol: the provider is holding some data, and the consumer wants to access it, and Oauth is used to manage the authorization of consumers.

        The Oauth-plugin gem can act as a provider (see this tutorial) or as a consumer (see the next tutorial).
        Devise is a completely different thing: it is an authentication system. It is used to manage your users and their credentials.
        Omniauth is also an authentication system, but it uses Oauth in consumer mode to perform the authentication: it tries to get access to your data in another application, and if the access is granted, it means that you were authenticated by the provider. It delegates the authentication to the provider. At the same time, it receives Oauth tokens that can be used to access the user’s data.

        Now, could you explain a bit more your use case?

  7. I have written an application in which I use Devise. I want to write a second application also uses Devise. I do not want to require users to log in to both applications. I want to log on only once and can use both applications. Additionally, in the second application users will have different roles, allowing access to different parts of the application.
    Users can not register themselves, are registered by the administrator, he can give them access to both applications or only one
    For example, if I log on to gmail I have access to all Google Apps. I do not have to login to each separately.
    I’m looking for something that will help me in creating such a connection between applications.
    One application keeps users and other log on to it.
    I hope I explained it well, but I’m not sure if it will work with OAuth or OmniAuth

    • This is another system:, called single sign on.

      If I understand correctly, you want two applications with different sets of users (users of the 1st, users of the 2nd, users of both). And an user of both applications must be able to log in an application, and be automatically logged in the other.

      There are multiple ways to do that.

      The first is to use a 3rd app, an external user manager (with Oauth or SAML), to authenticate the user. On the first login on one app, the user is redirected to the user manager, enters login and password, and is redirected to the app. If he wants to log on the 2nd app, he is redirected to the user manager, but doesn’t need to enter his credentials, and is automatically authenticated.

      The second, which may be simpler in your case, would be to share the database and the users table, and have the session cookies work on both sites (by putting them in subdomains).

  8. Hi,
    I am using following combinations for development
    Rails 3.1.2
    Oauth 0.4.4
    Oauth-plugin 0.4.0.rc2
    Devise
    I have created a custom provider and consumer

    Passing case
    I am logged in to consumer and I am trying to login to the consumer with provider, I am
    getting redirected to the provider, asking for the authorization form, I authorize and come back.

    Failing case
    I am *not* logged in to consumer and I am trying to login to the consumer with provider, I am
    getting redirected to the provider, asking for the authorization form
    and its redirecting me to page saying Validation failed, user can’t be blank.
    I have also added code in application controller for

    def current_user=(user)
    current_user = user
    end

    Please let me know in case I am missing something, as the access token generated is not able to identify the correct user who previously has created one.

  9. I am having trouble with the last part of your bug fix. The part where I have to replace a line in a file in the gem’s folder. I cannot find this folder, or a file that is named oauth_filter.rb.

    Could you tell me where I can locate that file?

    Thank you!

  10. Rails + oauth-plugin + mongodb part 1: Provider | Ruby on Rails freelancer

  11. Bonjour Géal!
    Thanks a lot for this tutorial. It was very useful and easy to follow, and saved me a lot of time.

    I’m a Rails newbie and am using Rails 3.2.3, and had to do the following additional changes to the code generated by oauth-plugin for it to work for OAuth 2.0. I’m posting it here to help others who might face the same problems.

    Problem 1. The error_messages_for method isn’t available anymore

    $ vi app/models/client_application.rb
    -> edit to use @client_application.errors instead

    Problem 2. Mandatory attr_accessible calls are missing.

    I ran into the problem in these 3 models.

    $ vi app/models/client_application.rb
    -> Add: attr_accessible :name, :url, :callback_url, :support_url

    $ vi app/models/oauth2_verifier.rb
    -> Add: attr_accessible :client_application, :user, :scope, :callback_url

    $ vi app/models/oauth2_token.rb
    -> Add: attr_accessible :user, :client_application, :scope

    Problem 3. Error in the OAuth 2 Authorize view.

    # undefined method `client_application’ for nil:NilClass

    $ vi app/views/oauth/oauth2_authorize.html.erb
    -> Change all @token.client_application to @client_application

    That’s all.

  12. this is really great. I’m not using devise, so I’ll have to look into getting this working with my authentication system properly. By the way, I’m thinking about using this with grape for api authentication. Do you think that’s a good idea?

    • That’s a good idea. Once you have a provider up and running, it shouldn’t be too hard to include OAuth token verification in your API.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s