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!
Get Test Driving Rails and make your tests faster and easier to maintain.