升级Rails 6后Devise OmniAuth Twitter出现NoMethodError求助
问题描述
已将Rails升级至6版本,同时升级了devise。在修复CSRF错误后,出现Twitter OAuth认证失败的问题,报错信息如下:
Started POST "/users/auth/twitter" for ::1 at 2022-09-22 21:24:44 -0400 (twitter) Request phase initiated. (twitter) Authentication failure! undefined method `downcase' for nil:NilClass "#{u.scheme.downcase}://#{u.host.downcase}#{(u.scheme.casecmp("http").zero? && u.port != 80) || (u.scheme.casecmp("https").zero? && u.port != 443) ? ":#{u.port}" : ""}#{u.path && u.path != "" ? u.path : "/"}" ^^^^^^^^^: NoMethodError, undefined method `downcase' for nil:NilClass Processing by Users::OmniauthCallbacksController#failure as HTML
相关配置
Gemfile
ruby "3.1.0" gem 'rails', '6.0.3.1' gem 'devise' gem 'omniauth-facebook' gem 'omniauth-twitter' gem "omniauth-rails_csrf_protection", "~> 1.0"
登录链接(适配CSRF改为POST方法)
<%= link_to "Log In", user_twitter_omniauth_authorize_path, method: :post, "data-turbo": false %>
omniauth.rb 配置
include Devise::OmniAuth::UrlHelpers OmniAuth.config.allowed_request_methods = [:post] OmniAuth.config.logger = Rails.logger if Rails.env.development? OmniAuth.config.full_host = Rails.env.production? ? 'https://www.website.com' : 'http://localhost:3000' Rails.application.config.middleware.use OmniAuth::Builder do provider :facebook, ENV["FACEBOOK_KEY"], ENV["FACEBOOK_SECRET"], callback_url: ENV["FACEBOOK_CALLBACK"] provider :twitter, ENV['TWITTER_API_KEY'], ENV['TWITTER_API_SECRET'], { secure_image_url: true, image_size: 'original' } provider :apple, ENV['APPLE_CLIENT_ID'], '', { scope: 'email name', team_id: ENV['APPLE_TEAM_ID'], key_id: ENV['APPLE_KEY_ID'], pem: Base64.strict_decode64(ENV['APPLE_P8_BASE64']) } end
Omniauth回调控制器
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController def self.provides_callback_for(provider) class_eval %Q{ def #{provider} @user = User.find_for_oauth(request.env["omniauth.auth"], current_user) skip_authorization authorize @user, policy_class: OmniauthCallbackPolicy if @user.persisted? @user.remember_me sign_in_and_redirect @user, event: :authentication set_flash_message(:notice, :success, kind: "#{provider}".capitalize) if is_navigational_format? else session["devise.#{provider}_data"] = request.env["omniauth.auth"] redirect_to new_user_registration_url end end } end [:twitter, :facebook, :apple].each do |provider| provides_callback_for provider end def failure redirect_to root_path end end
routes.rb 配置
devise_for :users, defaults: { format: :html }, controllers: { omniauth_callbacks: 'users/omniauth_callbacks', passwords: 'users/passwords' }
报错代码来自oauth gem的normalized_uri方法。
解决方案
这个错误是因为OAuth请求处理回调URL时无法正确获取scheme(http/https),导致u.scheme为nil,可按以下步骤修复:
给Twitter Provider指定完整回调地址
参照Facebook的配置,给Twitter provider添加callback_url参数,确保回调地址完整:provider :twitter, ENV['TWITTER_API_KEY'], ENV['TWITTER_API_SECRET'], { secure_image_url: true, image_size: 'original', callback_url: ENV["TWITTER_CALLBACK"] # 环境变量配置完整地址,比如http://localhost:3000/users/auth/twitter/callback }优化OmniAuth.full_host的设置
改用动态方式获取请求的scheme和host,避免硬编码导致的环境适配问题:OmniAuth.config.full_host = ->(env) { Rails.env.production? ? 'https://www.website.com' : "#{env['rack.url_scheme']}://#{env['HTTP_HOST']}" }更新相关gem版本
旧版本的omniauth-twitter和oauth gem可能存在POST请求下的URL解析bug,执行更新:# Gemfile中修改版本约束 gem 'omniauth-twitter', '~> 1.4' gem 'oauth', '~> 0.6.0'然后运行
bundle update omniauth-twitter oauth,重启服务器后测试。确认开发环境请求Host有效性
开发环境启动服务器时绑定0.0.0.0:rails s -b 0.0.0.0,访问时用http://0.0.0.0:3000,避免localhost解析异常导致的scheme缺失。
内容的提问来源于stack exchange,提问作者1dolinski
相关产品推荐
相关产品推荐

