2013-10-21 118 views
4

我有一個允許用戶發佈到LinkedIn,Facebook和Twitter的應用程序。我想授權每個用戶的帳戶與儘可能多的提供者作爲用戶的願望。Rails&Oauth:多個提供商

我的用戶模型有一些列,以幫助在一次授權一個供應商:

class User < ActiveRecord::Base 
    ... 
    attr_accessible :provider, :uid, :oauth_token, :oauth_expires_at, :oauth_token_secret, :access_token, :access_token_secret ... 
    ... 
end 

這裏的模型方法:

def self.from_omniauth(user, auth) 
    user.provider = auth.provider 
    if auth.provider == "facebook" 
    user.uid = auth.uid 
    user.oauth_token = auth.credentials.token 
    user.oauth_expires_at = Time.at(auth.credentials.expires_at) 
    elsif auth.provider == "twitter" 
    # not sure which one twitter uses 
    user.oauth_token = auth["credentials"]["token"] 
    user.oauth_token_secret = auth["credentials"]["secret"] 
    user.access_token = auth["credentials"]["token"] 
    user.access_token_secret = auth["credentials"]["secret"] 
    end 
    user.save! 
end 

而且控制器身份驗證方法是這樣的:

def authorise 
    user = User.from_omniauth(current_user, env['omniauth.auth']) 
    session[:user_id] = current_user.id 
    redirect_to root_url 
end 

任何幫助將是偉大的!真的不知道如何從這裏開始。有x(在上面的情況下3,更多來):provider列的數量似乎有點荒謬。

回答

5

關鍵是將驗證片與用戶模型本身分開,這樣您就可以在用戶和身份之間建立關係。這是我從一箇舊的項目Identity模型(使用omniauth):

class Identity < ActiveRecord::Base 
    belongs_to :user 

    attr_accessible :provider, :uid, 
        :description, :email, :first_name, :image, 
        :last_name, :location, :name, :nickname, 
        :phone, :raw_info, :urls 

    validates_presence_of :provider, :uid 
    validates_uniqueness_of :uid, scope: :provider 

    def self.find_with_omniauth(auth) 
    find_by_provider_and_uid(auth['provider'], auth['uid']) 
    end 

    def self.create_with_omniauth(auth) 
    create(provider:  auth['provider'], 
      uid:   auth['uid'], 
      name:   auth['info']['name'], 
      email:  auth['info']['email'], 
      nickname:  auth['info']['nickname'], 
      first_name: auth['info']['first_name'], 
      last_name: auth['info']['last_name'], 
      location:  auth['info']['location'], 
      description: auth['info']['description'], 
      image:  auth['info']['image'], 
      phone:  auth['info']['phone'], 
      urls:   auth['info']['urls'].to_json, 
      raw_info:  auth['extra']['raw_info'].to_json 
     ) 
    end 
end 

然後當然用戶模型應該引用:

class User < ActiveRecord::Base 
    ... 
    has_many :identities, dependent: :destroy 
    ... 

有很多的麻煩邊緣的情況下,當你允許多個omniauth提供程序進行登錄。因此,要建立一個新的登錄(會話),你可以這樣做:

class SessionsController < ApplicationController 

    def create 
    auth = request.env['omniauth.auth'] 
    origin = request.env['omniauth.origin'] 
    destination = origin.blank? ? root_path : origin 
    @identity = Identity.find_with_omniauth(auth) 
    @identity = Identity.create_with_omniauth(auth) if @identity.nil? 

    if signed_in? 
     if @identity.user == current_user 
     # Identity is already associated with this user 
     redirect_to destination, notice: "Already logged in and linked" 
     else 
     # Identity is not associated with the current_user 
     @old_user = @identity.user 
     if @old_user 
      current_user.posts << @old_user.posts 
      current_user.galleries << @old_user.galleries 
      current_user.favorites << @old_user.favorites 
     end 
     @identity.user = current_user 
     @identity.save() 
     @old_user.destroy if @old_user && @old_user.identities.blank? 
     redirect_to destination, notice: "Account was successfully linked" 
     end 
    else 
     if @identity.user.present? 
     # Identity has a user associated with it 
     self.current_user = @identity.user 
     redirect_to destination 
     else 
     # No user associated with the identity so create a new one 
     user = User.create_with_omniauth(auth['info']) 
     @identity.user = user 
     @identity.save() 
     self.current_user = @identity.user 
     redirect_to destination, notice: "Registration successful" 
     end 
    end 
    end 

    def destroy 
    self.current_user = nil 
    redirect_to root_url, notice: "Signed out successfully" 
    end 

    def omniauth_failure 
    origin = request.env['omniauth.origin'] 
    destination = origin.blank? ? root_path : origin 
    redirect_to destination, alert: "Connection failed" 
    end 
end 

我記得,我靠在這個話題寫了一段時間後,當我在寫上面的代碼的文章。請參閱https://github.com/intridea/omniauth/wiki/Managing-Multiple-Providers瞭解更多信息&想法。

+0

感謝您真正深入的回答!期待簽入。 –

+0

我對此的解讀出現了很不同,但是感謝這些想法。 –

+1

你沒有解決如何處理令牌。 –