Devise + Oauth - 仅在重定向第一次登录后的current_user nil

时间:2018-01-07 01:42:58

标签: ruby-on-rails ruby oauth devise

我在Ruby on Rails上使用Devise + OAuth。

我已经修改了ApplicationController中的after_sign_in_path:

def after_sign_in_path_for(resource)
  if session[:longitude]
    puts user_signed_in?
    new_questionnaire_path
  else
    super
  end
end

调用时,日志输出:

true
Redirected to http://localhost:3000/questionnaires/new

我在新的问卷调查路线上有一个事前的反应如下:

  def require_login
      unless current_user
        redirect_to new_user_registration_path, notice: 'Please sign in to get started!'
      end
  end

用户首次通过OAuth登录时,current_user以某种方式变为零,日志显示以下内容:

Started GET "/questionnaires/new" for 127.0.0.1 at 2018-01-06 17:30:50 -0800
Processing by QuestionnairesController#new as HTML
User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."id" IS NULL 
ORDER BY "users"."id" ASC LIMIT $1  [["LIMIT", 1]]
Redirected to http://localhost:3000/users/sign_up
Filter chain halted as :require_login rendered or redirected

如果用户第二次登录,则会将其重定向到new_questionnaire_path并且不会再次点击before_action。

为什么会发生这种情况?

编辑:这是我的OmniAuthCallbacksController的内容。我没有认为这是相关的,因为after_sign_in_path_for(资源)在sign_in_with(provider_name)之后被调用,但也许我错过了什么?

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
skip_before_action :verify_authenticity_token

  def sign_in_with(provider_name)
    @user = User.from_omniauth(request.env["omniauth.auth"])
    @user.profile
    sign_in_and_redirect @user, :event => :authentication
    set_flash_message(:notice, :success, :kind => provider_name) if 
    is_navigational_format?
  end

  def facebook
    sign_in_with "Facebook"
  end

  def linkedin
    sign_in_with "LinkedIn"
    @user.linked_in_data = request.env["omniauth.auth"]
    @user.save
    @profile = Profile.find_by_user_id(@user.id)
    @profile.first_name = @user.linked_in_data['info']['first_name']
    @profile.last_name = @user.linked_in_data['info']['last_name']
    @profile.title = @user.linked_in_data['info']['description']
    @profile.industry = @user.linked_in_data['extra']['raw_info']['industry']
    @profile.save
  end

  def twitter
    sign_in_with "Twitter"
  end

  def google_oauth2
    sign_in_with "Google"
  end

  def developer
    sign_in_with "Developer"
  end

end

2 个答案:

答案 0 :(得分:1)

您需要按照oauth devise implementation的说明进行操作。

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def facebook
    # You need to implement the method below in your model (e.g. app/models/user.rb)
    @user = User.from_omniauth(request.env["omniauth.auth"])

    if @user.persisted?
      sign_in_and_redirect @user, event: :authentication #this will throw if @user is not activated
      set_flash_message(:notice, :success, kind: "Facebook") if is_navigational_format?
    else
      session["devise.facebook_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end

  def failure
    redirect_to root_path
  end
end

我想问你在哪里找到这段代码。主要问题是我在error handling之后看不到User.from_omniauth的逻辑,所以也许某些内容失败并且没有触发错误。

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
skip_before_action :verify_authenticity_token

  def sign_in_with(provider_name)
    @user = User.from_omniauth(request.env["omniauth.auth"])
    @user.profile
    sign_in_and_redirect @user, :event => :authentication
    set_flash_message(:notice, :success, :kind => provider_name) if 
    is_navigational_format?
  end

我无法深入研究你的问题(登录后的重定向),因为我觉得你的逻辑错了。例如,如果您将def linkedin降为log in,我将始终运行@profile.first_name@profile.save即使用户未注册,但使用@user = User.from_omniauth(request.env["omniauth.auth"])登录

 def linkedin
    sign_in_with "LinkedIn"
    @user.linked_in_data = request.env["omniauth.auth"]
    @user.save
    @profile = Profile.find_by_user_id(@user.id)
    @profile.first_name = @user.linked_in_data['info']['first_name']
    @profile.last_name = @user.linked_in_data['info']['last_name']
    @profile.title = @user.linked_in_data['info']['description']
    @profile.industry = @user.linked_in_data['extra']['raw_info']['industry']
    @profile.save
  end

这是self.from_omniauth(auth)方法

def self.from_omniauth(auth)
  where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
    user.email = auth.info.email
    user.password = Devise.friendly_token[0,20]
    user.name = auth.info.name   # assuming the user model has a name
    user.image = auth.info.image # assuming the user model has an image
    # If you are using confirmable and the provider(s) you use validate emails, 
    # uncomment the line below to skip the confirmation emails.
    # user.skip_confirmation!
  end
end

答案 1 :(得分:0)

我能够通过根据Fabrizio的建议添加错误处理来解决这个问题,这表明@ user.persisted?是假的。

puts @ user.errors.to_a告诉我“密码不能为空”,我没有意识到这种情况,所以我将“user.password = Devise.friendly_token [0,20]”添加到自己身上。我的用户模型中的from_omniauth方法解决了这个问题。

我仍然不完全理解的是,在同一时间@ user.persisted中,如何将用户添加到数据库中?是假的。

下面显示的是我更新的控制器,其中包含错误处理,用于查找问题根源的puts语句,以及仅根据Fabrizio的优秀建议在第一次登录时执行配置文件更新的代码。

具有特别敏锐眼光的读者也可能会注意到sign_in_with方法中的“@ user.profile”行已被删除。这个方法用于在用户初次注册时为其构建配置文件,但我意识到在User模型上执行此操作作为after_create方法会更清晰,所以我也是这样做的。

class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  skip_before_action :verify_authenticity_token

  def sign_in_with(provider_name)
    @user = User.from_omniauth(request.env["omniauth.auth"])

    puts @user.errors.to_a

    if @user.persisted?
      sign_in_and_redirect @user, :event => :authentication
      set_flash_message(:notice, :success, :kind => provider_name) if is_navigational_format?
    else
      session["devise.#{provider_name}_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end

  def facebook
    sign_in_with "Facebook"
  end

  def linkedin
    sign_in_with "LinkedIn"
    @user.linked_in_data = request.env["omniauth.auth"]
    @user.save

    if @user.sign_in_count == 1
      @profile = Profile.find_by_user_id(@user.id)
      @profile.first_name = @user.linked_in_data['info']['first_name']
      @profile.last_name = @user.linked_in_data['info']['last_name']
      @profile.title = @user.linked_in_data['info']['description']
      @profile.industry = @user.linked_in_data['extra']['raw_info']['industry']
      @profile.save
    end

  end

  def twitter
    sign_in_with "Twitter"
  end

  def google_oauth2
    sign_in_with "Google"
  end

  def developer
    sign_in_with "Developer"
  end

  def failure
    redirect_to root_path
  end

end