You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby on Rails中Devise+OAuth首次登录后current_user为nil问题

首次OAuth登录后current_user为nil的问题排查与解决

问题分析

从你的代码和日志来看,核心矛盾是:首次通过OAuth登录时,after_sign_in_path_for中user_signed_in?返回true,但重定向到问卷页面后,before_action :require_login里的current_user却变成了nil;第二次登录则完全正常。

日志里的User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" IS NULL说明,Devise在尝试查找ID为NULL的用户——这意味着重定向后的请求会话中没有有效的用户ID,导致current_user无法加载。

根本原因

问题出在你的OmniAuthCallbacksController#linkedin方法的代码顺序上:
你先调用了sign_in_with(内部执行sign_in_and_redirect完成登录并重定向),之后才去修改用户的LinkedIn数据并保存。

这个顺序会导致:

  1. 浏览器收到重定向响应后,立刻发起问卷页面的请求;
  2. 此时你的后端还在执行sign_in_and_redirect之后的用户数据修改操作;
  3. 虽然用户已经被创建,但首次登录时的会话状态可能因后续的数据修改出现异常,或者会话未完全持久化就被重定向请求读取,导致无法识别用户。

解决方案

调整代码顺序,先完成用户和关联数据的所有持久化操作,再执行登录并重定向。同时重构代码逻辑,避免在登录后修改用户数据。

修改后的OmniAuthCallbacksController代码:

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

  # 重构sign_in_with,支持传入block在登录前处理用户数据
  def sign_in_with(provider_name)
    @user = User.from_omniauth(request.env["omniauth.auth"])
    @user.profile || @user.build_profile # 确保用户关联的Profile存在
    # 执行传入的自定义逻辑(比如第三方平台数据同步)
    yield if block_given?
    # 确保用户数据完全保存后再登录并重定向
    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" do
      # 先同步LinkedIn数据到用户模型
      @user.linked_in_data = request.env["omniauth.auth"]
      @user.save! # 用save!确保数据保存成功,避免静默失败

      # 同步Profile数据
      profile = @user.profile
      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
end

额外检查项

  1. 确保User.from_omniauth方法正确保存新用户
    检查你的User.from_omniauth方法,确保新创建的用户能成功持久化(比如必填字段如email是否存在):

    def self.from_omniauth(auth)
      user = where(provider: auth.provider, uid: auth.uid).first_or_initialize
      # 确保email字段有值(Devise默认要求)
      user.email = auth.info.email || "#{auth.uid}@#{auth.provider}.fake"
      user.save! # 用save!代替save,方便调试保存失败的情况
      user
    end
    
  2. 调试会话状态
    可以在after_sign_in_path_for和require_login中打印会话内容,确认用户ID是否正确传递:

    # 在ApplicationController的after_sign_in_path_for中添加
    puts "Session after sign in: #{session.inspect}"
    
    # 在QuestionnairesController的require_login中添加
    puts "Session in require_login: #{session.inspect}"
    

为什么第二次登录正常?

第二次登录时,用户已经存在于数据库中,from_omniauth直接找到现有用户,即使后续修改数据,会话中的用户ID已经和数据库中的用户完全匹配,因此重定向后的请求能正常加载current_user。

内容的提问来源于stack exchange,提问作者akoinesjr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:47:40