Notes to self

Sending JSON API like 401 Unauthorized error with Devise

If you are using Ruby on Rails together with Devise gem you might be wondering how to handle unauthorized error responces in your JSON API.

JSON API prescribes that to send errors you need a root “errors” array of errors with as least a status code (as string) and a title (as string). Essentially that means to turn our error message issued by Devise into the following:

{"errors":[{"status":"401","title":"Unauthorized"}]}

The problem is that Devise does not really throw an exception that we could rescue_from from our controller. What we need is to create a custom equivalent of Devise::FailureApp:

# Our custom failure response app since we want to return JSON:API like
# messages for some APIs.
module MyApp
  class FailureApp < Devise::FailureApp
    def respond
      if request.controller_class.to_s.start_with? 'API::'
        json_api_error_response
      else
        super
      end
    end

    def json_api_error_response
      self.status        = 401
      self.content_type  = 'application/json'
      self.response_body = { errors: [{ status: '401', title: i18n_message }]}.to_json
    end
  end
end

In the respond method we can handle special cases by quering information from the request object. In our example we will respond differently in case where the controller is one of our API controllers. (This is because basing it on being a JSON requests would backfire for our AJAX endpoints. Feel free to adjust this to your needs.)

Once we have that in place we just need to tell Devise to actually use it which can be done within Devise initializer:

Devise.setup do |config|
  ...
  config.warden do |manager|
     manager.failure_app = MyApp::FailureApp
  end
  ...
end

And that’s it!

Check out my book
Interested in Ruby on Rails default testing stack? Take Minitest and fixtures for a spin with my latest book.

Get Test Driving Rails and make your tests faster and easier to maintain.

by Josef Strzibny
RSS