Skip to content

Instantly share code, notes, and snippets.

@zernel
Created August 24, 2012 01:45
Show Gist options
  • Save zernel/3444613 to your computer and use it in GitHub Desktop.
Save zernel/3444613 to your computer and use it in GitHub Desktop.
Devise authentication with multiple provider
class Authorization < ActiveRecord::Base
attr_accessible :provider, :uid, :user_id
belongs_to :user, :inverse_of => :authorizations
end
Devise.setup do |config|
...
# These oauth key and secret are use for localhost. Please replace them if necessary
config.omniauth :github, 'a55eb780a5a66bc2fabe', 'b531490bdcc4fcbb3e05bdca2e75e60b8ad9ba12'
config.omniauth :facebook, "283956394958478", "fdfedf11fba5c4e77e87a748aad36cc9"
config.omniauth :google_oauth2, '950211539633-6sh58gmv2mc39jn8m6b6uo9i5po6qkjh.apps.googleusercontent.com', 'pvDaViwlfKROcMTxhauo76Gu'
#config.omniauth :twitter, "xfOsUPR6WPYzs3EXBbrag", "jxWQdnKr0KnzteXZHbVaE2JtbE6PzuKjOs9PnkeEag"
end
gem 'devise'
gem 'omniauth'
gem 'omniauth-github'
gem "omniauth-twitter"
gem "omniauth-facebook"
gem "omniauth-google-oauth2"
# encoding: utf-8
class User
module OmniauthCallbacks
["github","google_oauth2","twitter","facebook"].each do |provider|
define_method "find_or_create_for_#{provider}" do |response|
uid = response["uid"]
data = response["info"]
if user = Authorization.where("provider" => provider , "uid" => uid).first.try(:user)
user
elsif user = User.find_by_email(data["email"])
user.authorizations << Authorization.new(:provider => provider, :uid => uid )
user
else
user = User.new(email: data["email"], username: data['nickname'] || data['name'])
if user.save(:validate => false)
user.authorizations << Authorization.new(:provider => provider, :uid => uid )
return user
else
Rails.logger.warn("User.create_from_hash 失败,#{user.errors.inspect}")
return nil
end
end
end
end
end
end
# encoding: utf-8
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def self.provides_callback_for(*providers)
providers.each do |provider|
class_eval %Q{
def #{provider}
if not current_user.blank?
current_user.bind_service(env["omniauth.auth"])#Add an auth to existing
redirect_to edit_user_registration_path, :notice => "成功绑定了 #{provider} 帐号。"
else
@user = User.find_or_create_for_#{provider}(env["omniauth.auth"])
if @user.persisted?
flash[:notice] = "Signed in with #{provider.to_s.titleize} successfully."
sign_in_and_redirect @user, :event => :authentication, :notice => "登陆成功。"
else
redirect_to new_user_registration_url
end
end
end
}
end
end
provides_callback_for :github, :twitter, :google_oauth2, :facebook
# This is solution for existing accout want bind Google login but current_user is always nil
# https://github.com/intridea/omniauth/issues/185
def handle_unverified_request
true
end
end
class Users::RegistrationsController < Devise::RegistrationsController
def update
self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)
if resource.update_without_current_password(resource_params)
if is_navigational_format?
flash_key = update_needs_confirmation?(resource, prev_unconfirmed_email) ?
:update_needs_confirmation : :updated
set_flash_message :notice, flash_key
end
sign_in resource_name, resource, :bypass => true
respond_with resource, :location => after_update_path_for(resource)
else
clean_up_passwords resource
respond_with resource
end
end
protected
def update_needs_confirmation?(resource, previous)
resource.respond_to?(:pending_reconfirmation?) &&
resource.pending_reconfirmation? &&
previous != resource.unconfirmed_email
end
end
devise_for :users, controllers: { omniauth_callbacks: "users/omniauth_callbacks" }
rails g model Authorization provider:string user_id:integer uid:string
rake db:migrate
class User < ActiveRecord::Base
extend OmniauthCallbacks
has_many :authorizations, :dependent => :destroy
...
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
...
def bind_service(response)
provider = response["provider"]
uid = response["uid"]
authorizations.create(:provider => provider , :uid => uid )
end
def update_without_current_password(params, *options)
params.delete(:current_password)
if params[:password].blank?
params.delete(:password)
params.delete(:password_confirmation) if params[:password_confirmation].blank?
end
result = update_attributes(params, *options)
clean_up_passwords
result
end
end
class CreateAuthorizations < ActiveRecord::Migration
def change
create_table :authorizations do |t|
t.string :provider
t.integer :user_id
t.string :uid
t.timestamps
end
end
end
@sundevilyang
Copy link

你好哈,我按照你的gist来按照,运行的时候出现了一个问题。 多谢 :)

Processing by Users::OmniauthCallbacksController#github as HTML
  Parameters: {"code"=>"043082ff642d4dbd1aeb", "state"=>"1457d8515315dedfa569edf6ce67d45c894870d5c6708004"}
Completed 500 Internal Server Error in 1ms

NoMethodError (undefined method `find_or_create_for_github' for #<Class:0x007fd8854bca70>):

@sundevilyang
Copy link

zernel , 由于粗心,没有更改user.rb。 但是现在又有新的问题出现了,很是头疼。

rake db:migrate
rake aborted!
Expected /Users/Young/sites/mygroup/app/models/omniauth_callbacks.rb to define OmniauthCallbacks
=> Booting Thin
=> Rails 3.2.8 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
Exiting
/Users/Young/.rvm/gems/ruby-1.9.3-p194/gems/activesupport-3.2.8/lib/active_support/dependencies.rb:503:in `load_missing_constant': Expected /Users/Young/sites/mygroup/app/models/omniauth_callbacks.rb to define OmniauthCallbacks (LoadError)

我把你文件的安装位置也发上来,你看看哈。
/app/controllers/users/omniauth_callbacks_controller.rb
/app/controllers/users/registrations_controller.rb
/app/models/omniauth_callbacks.rb
/app/models/user.rb
/config/initializers/devise.rb
谢谢你抽空答疑:)

@zernel
Copy link
Author

zernel commented Oct 4, 2012

OmniauthCallbacks这个定义在scope下,应该要放在/app/models/user/omniauth_callbacks.rb中,非常抱歉,上面没有注明:)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment