如何解决OmniAuth::Strategies::OAuth2::CallbackError?

42
我正在构建一个使用Omniauth进行登录服务的Rails应用程序。为了验证Google,我正在使用OmniAuth Google OAuth2 Strategy
当用户点击“允许访问”按钮时,一切正常。但是当用户点击“不,谢谢”按钮时,会引发下面的错误。
OmniAuth::Strategies::OAuth2::CallbackError

我尝试在应用程序控制器中添加以下的救援代码。
class ApplicationController < ActionController::Base
  rescue_from OmniAuth::Strategies::OAuth2::CallbackError, :with =>
    :omniauth_callback_error_handler

 protected

 def omniauth_callback_error_handler
  redirect_to init_sign_in_users_path
 end
end

但是没有运气。有什么想法吗?
4个回答

66

您可以以更简洁的方式设置omniauth初始化器中的on_failure proc:

OmniAuth.config.on_failure = UsersController.action(:oauth_failure)

1
谢谢您提供这个干净利落的解决方案!;) - Dmitri
3
这段代码在生产环境下运行良好。但是,如果你在开发环境下更改了你的UsersController#oauth_failure代码,你将看不到任何更改。Soundar基于Proc的代码在开发中更好,并且在开发中按预期工作。 - mcmlxxxiii
1
@mcmlxxxiii 说得很好。当我采用这种方法时,在使用 Spork 进行测试时出现了问题。这一行导致我的 UsersController 和 ApplicationController(UsersController 的超类)在每次测试运行时都没有被重新加载。采用 soundar 的解决方案解决了这个问题。请注意,我不必执行字符串常量化步骤;我能够直接在 Proc.new 块中引用 UsersController,因为它只在稍后被评估。 - Liron Yahdav
1
一个需要注意的地方是,这不会直接将提供程序名称发送到控制器,您需要执行类似于 request.path.split('/').third 的操作来获取它。 - localhostdotdev
2
这段代码在Rails 6中会导致DEPRECATION WARNING,因为控制器将在初始化过程中被初始化。此版本避免了这个问题:https://dev59.com/x2gv5IYBdhLWcg3wQ-gV#10743341 - Fabio Gomes

42
这是因为身份验证是在中间件中进行的,所以您的控制器不参与其中。这是引发异常的 位置,而调用的代码是这个
我认为您可以通过在OmniAuth初始化程序中定义一个回调函数来处理此类错误,使用以下代码。
OmniAuth.config do |config|
  config.on_failure do
    # your handling code invoked in the context of a rack app
  end
end
否则,三个月前有 一次提交 引入了这种行为。
def redirect_to_failure
  message_key = env['omniauth.error.type']
  new_path = "#{env['SCRIPT_NAME']}#{OmniAuth.config.path_prefix}/failure?message=#{message_key}"
  Rack::Response.new(["302 Moved"], 302, 'Location' => new_path).finish
end

这意味着在出现错误时,用户将被重定向到/auth/failure页面,并显示一个错误消息。因此,您应该能够为该路径定义一个路由并在应用程序中处理它。请记住,这不会在开发模式下发生,所以您需要在其他环境中尝试它。如果在生产中没有发生,请尝试将omniauth gem升级到1.1.0版本。


18
1000.times.do puts "Thank you very much!" end 一千次执行 输出 "非常感谢!" 结束 - Soundar Rathinasamy
3
我用OmniAuth代替了Omniauth,现在能够正常工作了。看起来大小写发生了变化。 - Tyler
4
为了在开发模式下测试失败,你可以添加以下代码: OmniAuth.config.failure_raise_out_environments = [] 该代码的作用是禁用 OmniAuth 在特定环境下抛出异常。 - David Radcliffe
1
如果您无法在开发环境中进入操作,则请查看 https://github.com/omniauth/omniauth/wiki/FAQ#omniauthfailureendpoint-does-not-redirect-in-development-mode。 - Amit Patel

18

我已经按照Fabio的第一个建议解决了这个问题。

OmniAuth.config.on_failure = Proc.new do |env|
  UsersController.action(:omniauth_failure).call(env)
  #this will invoke the omniauth_failure action in UsersController.
end

在我的UsersController中

class UsersController < ActionController::Base
  def omniauth_failure
    redirect_to init_sign_in_users_path
    #redirect wherever you want.
  end
end

4
我应该把上面的代码放在哪里?Omniauth.config.on_failure = Proc.new do |env| "UsersController".constantize.action(:omniauth_failure).call(env)

这将调用 UsersController 中的 omniauth_failure 动作。

end
- regmiprem
2
将代码放入config/initializers/omniauth_failure_callback.rb文件中。我建议您使用Peter的代码,因为它非常干净。 - Soundar Rathinasamy
2
@soundar,尽管Peter的代码更简洁,但是你的代码在开发环境中按预期工作。所以你的答案也得到了+1的支持。 - mcmlxxxiii
正如@mcmlxxxiii所提到的,这对于开发和测试来说更好。特别是如果您正在使用Spork,另一种解决方案将防止UsersController(以及任何它继承的控制器)在每次测试运行时重新加载。但我不必调用““UsersController”。constantize”。我能够直接调用UsersController.action,因为这段代码在稍后执行的块中。 - Liron Yahdav
谢谢。请将 Onmiauth 更改为 OmniAuth - Sergey Makridenkov
同其他答案:一个需要注意的地方是,这不会直接将提供程序名称发送到控制器,您需要执行类似于 request.path.split('/').third 的操作来获取它。 - localhostdotdev

2

有一种配置可以使用/auth/failure代替抛出错误。

我使用的是OmniAuth 1.2.2,当我检查FailureEndpoint时,我发现代码类似于this

def call
  raise_out! if OmniAuth.config.failure_raise_out_environments.include?(ENV['RACK_ENV'].to_s)
  redirect_to_failure
end

failure_raise_out_environments在此处定义

def self.defaults
  @defaults ||= {
    # other configurations
    :failure_raise_out_environments => ['development']
  }
end

环境可以进行配置,这样解决方案就会变得简单。我使用Rails,所以我将以下代码放在初始化文件中:

OmniAuth.configure do |config|
  # Always use /auth/failure in any environment
  config.failure_raise_out_environments = []
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接