2

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.

GGizmos
  • 3,443
  • 4
  • 27
  • 72
  • 'I think authenticate_user is returning my "unauthorized" web page to the caller' - this is is really easy to test. Just pop open the network tab in the inspector in your browser. – max Oct 20 '19 at 19:36
  • I know for certain that a web page is being returned to the caller. I can't trace into the controller method because it never runs if authenticate_user! fails, but I'm pretty sure that's what going on. – GGizmos Oct 20 '19 at 20:21

1 Answers1

2

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 
max
  • 96,212
  • 14
  • 104
  • 165
  • helpful, but how to I determine if the request is ajax or not? I tried using request.xhr? but it returns nil in all cases including xhr requests from angular $http methods. – GGizmos Oct 20 '19 at 21:14
  • What do the headers of the request look like? I have never actually used angular so I have no clue what it sends. – max Oct 20 '19 at 21:16
  • `request.xhr?` just checks for the `'HTTP_X_REQUESTED_WITH'` header that is commonly sent by many (but not all) Ajax frameworks. https://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-xml_http_request-3F – max Oct 20 '19 at 21:26
  • right. Having a little trouble answering your question "What do the headers look like". I am examining the request object but request.headers has among other things a hash of 84 values which won't fit in the comment box here. I was looking for what 'HTTP_X_REQUESTED_WITH but I'm not seeing it. . . – GGizmos Oct 20 '19 at 21:34
  • Best I can tell according to other posts, angular requests via $http do not provide HTTP_X_REQUESTED_WITH in the header. – GGizmos Oct 20 '19 at 21:51
  • Ok, I would just configure it to do so if you can control the client code. https://stackoverflow.com/questions/18168836/angularjs-resource-not-sending-x-requested-with – max Oct 20 '19 at 21:52