42

I am building a Rails application with Omniauth for log in service.To authenticate Google I am using OmniAuth Google OAuth2 Strategy.

When user clicks 'allow access' button everything works fine.But when user clicks 'no thanks' button the below error is raised.

OmniAuth::Strategies::OAuth2::CallbackError

I have tried adding the below rescue code in application controller.

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

But no luck. Any idea?

Christopher Oezbek
  • 23,994
  • 6
  • 61
  • 85
Soundar Rathinasamy
  • 6,658
  • 6
  • 29
  • 47

4 Answers4

65

You can set the on_failure proc in the omniauth initializer in an even cleaner fashion:

OmniAuth.config.on_failure = UsersController.action(:oauth_failure)
Nick
  • 9,493
  • 8
  • 43
  • 66
Peter P.
  • 3,221
  • 2
  • 25
  • 31
  • 1
    Thank you for this clean solution! ;) – Dmitri Nov 12 '12 at 08:22
  • 3
    This code works great in production evironment. However, if you change your UsersController#oauth_failure code in development environment, you'll not see any changes. The soundar's code based on Proc is better in development and works as expected in development. – mcmlxxxiii Feb 25 '13 at 17:27
  • 1
    @mcmlxxxiii has a good point. When I went with this approach I had issues in test when using Spork. This line was causing my UsersController and ApplicationController (UsersController's superclass) not to get reloaded on each test run. Going with soundar's solution fixed this. Note that I didn't have to do the string constantize step; I was able to reference the UsersController directly in the Proc.new block because it only gets evaluated later. – Liron Yahdav Mar 11 '13 at 21:28
  • 1
    one gotcha is that this doesn't send the provider name directly to the controller, you need to do something like `request.path.split('/').third` to get it – localhostdotdev Apr 10 '19 at 07:39
  • 2
    This code on Rails 6 will result in a DEPRECATION WARNING as the controller will be initialized in the initialization process. This version avoids this problem: https://stackoverflow.com/a/10743341/727 – Fabio Gomes Aug 04 '19 at 14:00
41

This happens because the authentication happens in a middleware so your controller is not involved in it. This is where the exception is raised and the called code is this

I think you can handle this kind of error by defining a callback in OmniAuth initializer with this kind of code

OmniAuth.config do |config|
  config.on_failure do
    # your handling code invoked in the context of a rack app
  end
end

Otherwise there is a commit of three months ago which introduce this behavior

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

which states that on errors your user is redirected to /auth/failure with an error message, so you should be able to define a route for that path and handle it in your app. Keep in mind that this won't happen in development mode so you need to try it in other envs. If this doesn't happen in production try to upgrade your omniauth gem to version 1.1.0

Brian Low
  • 11,605
  • 4
  • 58
  • 63
Fabio
  • 18,856
  • 9
  • 82
  • 114
18

I have solved this problem with the Fabio's first suggestion.

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

In my UsersController

class UsersController < ActionController::Base
  def omniauth_failure
    redirect_to init_sign_in_users_path
    #redirect wherever you want.
  end
end
John Naegle
  • 8,077
  • 3
  • 38
  • 47
Soundar Rathinasamy
  • 6,658
  • 6
  • 29
  • 47
  • 4
    where do i have to put that above code Omniauth.config.on_failure = Proc.new do |env| "UsersController".constantize.action(:omniauth_failure).call(env) #this will invoke the omniauth_failure action in UsersController. end – regmiprem Nov 30 '12 at 09:17
  • 2
    put the code in config/initializers/omniauth_failure_callback.rb file. I suggest you to use the peter's code as its really clean. – Soundar Rathinasamy Nov 30 '12 at 14:30
  • 2
    @soundar, though peter's code is cleaner, your code works in development environment as expected in development environment. So +1 to your answer too. – mcmlxxxiii Feb 25 '13 at 17:29
  • as @mcmlxxxiii mentioned, this is better for development and test. Especially if you're using Spork, the other solution will prevent UsersController (and any controllers it inherits from) not to get reloaded on each test run. But I didn't have to call `"UsersController".constantize`. I was able to directly call `UsersController.action` because this code is in a block that gets executed later. – Liron Yahdav Mar 11 '13 at 21:30
  • Thank you. Please change `Onmiauth` to `OmniAuth` – Sergey Makridenkov May 06 '14 at 06:37
  • Same as other answer: one gotcha is that this doesn't send the provider name directly to the controller, you need to do something like `request.path.split('/').third` to get it – localhostdotdev Apr 10 '19 at 07:43
1

There's a configuration to use /auth/failure instead of raising an error.

I use OmniAuth 1.2.2 and when I checking the FailureEndpoint I found the code is like this:

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

And the failure_raise_out_environments is defined here:

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

The environment can be configured so the solution is easy. I use Rails so I put below code in an initializer file:

OmniAuth.configure do |config|
  # Always use /auth/failure in any environment
  config.failure_raise_out_environments = []
end
darkbaby123
  • 1,915
  • 17
  • 15