How to handle authentication failures on ajax calls?

Viewed 184

My rails controller has two types of handlers, one type is conventional response with a web page, another is designed to respond to $http get requests from Angular, and returns json to be processed by the pages javascript code.

I use devise, and I this code at the top of my application controller

 protect_from_forgery
 before_action :authenticate_user!
 after_action :set_csrf_cookie

The problem is that when for example, the login goes stale, I think authenticate_user is returning my "unauthorized" web page to the caller, rather than the json that would inform the caller that the current user is no longer authorized, and then I could handle the condition on the client side properly.

Any thoughts on efficient way to do this, withoug having to take out authenticate_user! from the application controller.

Most of my controllers have handlers for about 15 routes, about 50/50 which of them are designed to return json to ajax calls, and the others return web pages. I like the security that authenticate_user! in the application controller provides, and am hesitant to remove it and instead have to have different code to handle security in each of my methods.

Thanks.

1 Answers

To understand how this works you really got to get into Warden (which devise is built on top of) and Rack. What before_action :authenticate_user! does is call Warden.authenticate! and asks it to identify a user. Warden identifies users by using strategies. A strategy can be just using session[:user_id] to find a user from the database (which what happens 99% of the time in Devise) or something more novel like HTTP Basic Auth.

If all the available strategies fail then the failure app is called. This is a Rack application. In Devise this is just a basic Rails controller (Rails controllers are Rack compliant applications) that usually returns a redirect. If you are running Rails in the development environment you may get a HTML response though as the error handler that shows you those friendly little exception pages kicks in.

You can customize the response by providing your own failure application:

class CustomAuthFailure < Devise::FailureApp
  def respond
    self.status = 401 
    self.content_type = 'json'
    self.response_body = {"errors" => ["Invalid login credentials"]}.to_json
  end 
end
# config/initializers/devise.rb
config.warden do |manager|
  manager.failure_app = CustomAuthFailure
end 
Related