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'
inconfig/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