Devise / OmniAuth:用于注册和登录的不同逻辑

时间:2015-04-09 17:18:46

标签: ruby-on-rails ruby-on-rails-4 devise omniauth

默认设计授权路径(即提及here的user_omniauth_authorize_path)似乎旨在用于OmniAuth注册和登录。当在OmniauthCallbacksController中收到auth时,您的rails应用程序通常使用auth信息来创建新用户或更新现有用户,无论用户是打算登录还是注册服务(即Facebook)。

我的利益相关者已经请求了auth和login的不同行为。如果用户点击"注册Facebook"并且他们的Facebook电子邮件没有帐户,他们希望继续使用该电子邮件创建帐户。但是,如果用户点击"使用Facebook登录"他们的Facebook电子邮件没有帐户,他们希望向用户显示一条错误消息,解释"您使用的Facebook帐户与我们记录中的帐户不匹配。请使用您的barre3电子邮件和密码"

登录

这种逻辑的地方似乎是在OmniauthCallbacksController的Facebook方法中。将这个回调方法传递给用户的意图('登录'或注册')的最简洁方法是什么?

3 个答案:

答案 0 :(得分:3)

您可以通过在授权网址上添加一些参数来从授权中获取参数以指示用户的意图。

例如:

- if devise_mapping.omniauthable?
  - resource_class.omniauth_providers.each do |provider|
    = link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider, {intent: :sign_in})

(这会创建一个类似于:http://whatevs.dev/users/auth/facebook?intent=sign_in

的网址

然后,在回调控制器中(无论你怎么命名):

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def facebook
    if env["omniauth.params"]["intent"] == "sign_in"
      # Don't create a new identity
    else
      # Normal flow, such as:
      @user = User.from_omniauth(request.env["omniauth.auth"])
      sign_in_and_redirect @user
    end
  end
end

答案 1 :(得分:1)

  

如果用户点击&#34;注册Facebook&#34;并且他们的Facebook电子邮件没有帐户,他们希望继续使用该电子邮件创建帐户

此假设无效,因为Facebook只能使用电话号码创建。即使用户有电子邮件,也需要额外的许可才能从Facebook获取用户的电子邮件。 您的应用程序应验证Facebook API而不是电子邮件返回的facebook_uid。

  

将这个回调方法传递给用户的意图(&#39;登录&#39;或注册&#39;)的最简洁方法是什么?

对于OmniAuth,登录&#39;登录&#39;或者&#39;注册&#39;。它所做的就是尝试使用提供的Facebook令牌对用户进行身份验证。 区分的一种简洁方法是在控制器级别上进行分离。如果用户尝试登录,请调用SessionsController #create,如果用户尝试注册,请调用UsersController #create。

答案 2 :(得分:0)

我也对答案进行了过多的研究,但没有得到满意的答案,因此我向前迈了一步,并创建了可按需工作的功能。 @Jason的答案可以帮助我到达此处。

视图/用户/会话/new.html.erb

<% if devise_mapping.omniauthable? %>
  <% resource_class.omniauth_providers.each do |provider| %>
    <%= link_to omniauth_authorize_path(resource_name, provider, {intent: :sign_in}) do %>
      <i class="fa fa-<%= provider.to_s.split('_')[0] %>"></i>
    <% end %>
  <% end %>
<% end %>

视图/用户/注册/new.html.erb

<% if devise_mapping.omniauthable? %>
  <% resource_class.omniauth_providers.each do |provider| %>
    <%= link_to omniauth_authorize_path(resource_name, provider, {intent: :sign_up}) do %>
      <i class="fa fa-<%= provider.to_s.split('_')[0] %>"></i>
    <% end %>
  <% end %>
<% end %>

控制器/用户/omniauth_callbacks_controller.rb

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

  def self.provides_callback_for(provider)
    class_eval %Q{
      def #{provider}
        if request.env["omniauth.params"]["intent"] == "sign_up"
          @user = User.from_omniauth_sign_up(request.env['omniauth.auth'])
        elsif request.env["omniauth.params"]["intent"] == "sign_in"
          @user = User.from_omniauth_sign_in(request.env['omniauth.auth'])
        end
        
        if @user.present?
          sign_in_and_redirect @user, event: :authentication
          set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format?
        else
          flash[:errors] = {:email => "is not registered with us."}
          session["devise.#{provider}_data"] = request.env["omniauth.auth"]
          redirect_to new_user_registration_url
        end
      end
    }
  end

  [:google_oauth2, :facebook, :linkedin].each do |provider|
    provides_callback_for provider
  end

end

models / user.rb

def self.from_omniauth_sign_up(auth)
  # check if email address obtained from auth server is in User table
  user = User.where(email: auth[:info][:email]).first
  if user.present?
    user.first_name = auth.info.first_name
    user.last_name  = auth.info.last_name
    user.image      = auth.info.image
    user.provider   = auth.provider
    user.uid        = auth.uid
    user.save   
  else
    user            = User.new(provider: auth.provider, uid: auth.uid)
    user.email      = auth.info.email
    user.username   = "#{auth.uid}-#{auth.provider}"
    user.password   = Devise.friendly_token[0,20]
    user.first_name = auth.info.first_name
    user.last_name  = auth.info.last_name
    user.image      = auth.info.image
    user.status_id  = 2
    user.skip_mobile_validation = true
    user.skip_confirmation!
    user.save
  end
  user
end

def self.from_omniauth_sign_in(auth)
  if user = User.where(email: auth[:info][:email]).first
    user
  end
end