Rails + oauth-plugin + mongodb – Part 1: Provider

Recently I had a chance to work with Oauth and Rails using oauth-plugin and mongodb. Although oauth-plugin itself has a documentation but actually doesn’t cover the mongoid part detailed enough. Also I found this very good tutorial but it’s for SQLite3 only: Oauth Provider, Oauth Consumer

Base on the above posts, to get Rails and oauth-plugin working with mongodb, you need to do some changes.

Firstly, create a oauth-sample directory, you’ll put provider and consumer apps in there.

1. Create new project

    • mkdir oauth
    • cd oauth
    • rails new provider
    • cd provider
    • add these gems to your Gemfile
gem 'rails', '3.0.12'
gem 'devise'
gem 'oauth-plugin', '~> 0.4.0'
gem 'mongoid' gem 'bson_ext'
  • bundle install`

Now you have:

  • an authentication system installed (Devise)
  • oauth-plugin which helps you a lot in generating files for Oauth
  • mongoid a driver for mongodb
  • bson_ext which mainly for mongo peformance improvement

2. Config the Rails app to use mongodb

  • delete config/database.yml
  • rails g mongoid:config
  • replace require 'rails/all' in config/application.rb with
require "actioncontroller/railtie"
require "actionmailer/railtie"
require "activeresource/railtie"
require "rails/testunit/railtie"
#require "sprockets/railtie" # Uncomment this line for Rails 3.1+

3. Config the generators

This step is important, you need to do this before generating/installing Devise model, Oauth things…

Add this code into your application.rb, uncomment haml/rspec if you use them. Keep the mongoid option as that is what we want.

config.generators do |g|
  g.orm :mongoid
  #g.template_engine :haml
  #g.test_framework :rspec
end

From now on, the generators know they should invoke mongoid instead of activerecord when generating things…

4. Generates things

rails g devise:install
rails g devise User
rails g controller Data index
rails g oath_provider
rm public/index.html

Add this into your User model:

references_many :client_applications
references_many :tokens, :class_name => "OauthToken", :order => "authorized_at desc"

Add this into your oauth_clients_controller.rb:

alias :login_required :authenticate_user!

In oauth_clients_controller.rb, find and replace the line reads

@tokens = current_user.tokens.find(:all, :conditions => 'oauth_tokens.invalidated_at is null and oauth_tokens.authorized_at is not null')

with

@tokens = current_user.tokens.includes(:client_application).where('oauth_tokens.invalidated_at is null and oauth_tokens.authorized_at is not null')

In config/application.rb, add this code:

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

In app/controllers/oauth_controller.rb, add this code:

alias :logged_in? :user_signed_in?
alias :login_required :authenticate_user!

Modify the Data#index to add some data:

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

In config/routes.rb, add your root path root :to => "data#index"

5. Explain :include issue

As mentioned in oauth-plugin guide we should use

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

Actually :include is for ActiveRecord eager-loading feature and it doesn’t work with Mongoid, so I thought I should find an equivalent of it in Mongoid.

After doing a research I found that Mongoid originally didn’t support eager-loading, then started supporting eager-loading from version 2.2.0

We can use this feature by using Model#includes method as guided here

So I think we should remove the :include => [:client_application] option in User model, then add .includes(:client_application) to the controller as above.

6. Issues with oauth-plugin + mongoid

You can make these below changes yourself or simply get from here. I folked from original repo and modified.

You will encounter these one when you trying to connect consumer with provider on step 5 in consumer guide.
– Dynamic finders don’t seem to work. You’ll get the exception with ‘findbykey’, we need to modify the oauth-plugin gem directly (as the author hasn’t provide a fix yet).
Do this commands:

bundle show oauth-plugin # to know where it was installed.
cd /path/to/oauth-plugin/installed
cd lib
grep -rn 'find_by_' .

Let’s change all the .find_by_<a_key>(<a_value>) with .where(:<a_key> => <a_value>).first

E.g:

#this one
@token = ::RequestToken.findbytoken! params[:oauth_token]
#should be changed to
@token = ::RequestToken.where(:token => params[:oauth_token]).first

undefined method 'expand_complex_criteria' for #<Array:0x00000102934308>

In lib/oauth/rack/oauth_filter.rb of oauth-plugin gem, find the line (around line #27) which reads:

if token = Oauth2Token.first(:conditions => ['invalidated_at IS NULL AND authorized_at IS NOT NULL and token = ?', token_string])

change it to

if token = Oauth2Token.where(:invalidated_at => nil, :authorized_at.ne => nil, :token => token_string).first

Then find the line (around #45) which reads:

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

change it to

oauth_token = client_application.tokens.where(:token => request_proxy.token).first

And we’re done for Provider

You can get the source code for Provider here

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