2013-10-21 75 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

你没有解决如何处理令牌。 –