Question

I want to build a rails app with two different protect_from_forgery strategies: one for the web application, and one for the API.

In my application controller I have this line of code: protect_from_forgery with: :exception in order to prevent CSRF attacks, it works just fine.

In my API namespace, I created an api_controller that inherits from my application controller, and that is the parent class of all the other controllers in the API namespace, and I changed the code above with: protect_from_forgery with: :null_session.

Sadly, I have an error when trying to make POST request: "Can't verify CSRF token authenticity".

I don't want to skip the verify_authenticity_token method in my API controllers, I just want to have two distinct strategies in my app, so how do I override the protect_from_forgery strategy defined in my application controller ?

Edit: Ok, so I eventually did what I did not want to do in the first place: change the inheritance of my api_controller: it now inherits from ActionController::Base, and no longer from my application controller. It does work now but:

  1. It does not answer my question i.e. overriding the protect_from_forgery strategy.
  2. It is not DRY as I have to copy/past what was previously in my application_controller.

So if anyone has a real way to overwrite this method, I'd appreciate it.

Was it helpful?

Solution

What if you leave the protect_from_forgery with: :exception in the application controller but then you put the following in your API controller?

skip_before_action :protect_from_forgery
protect_from_forgery with: :null_session

That way, you still get the standard CSRF attack protection for all controllers in your web application but you also get the null session behavior for your API methods.

OTHER TIPS

I am running an application with a similar structure - Web App + API. I solved the CSRF problem like this:

  • Apply protect_from_forgery only for non API requests
  • My API endpoint is api.example.com, so I used subdomain constraint to distinguish API and web app requests

Code:

class ApplicationController < ActionController::Base

  protect_from_forgery with: :exception, if: :isWebRequest?

  def isWebRequest?
    request.subdomains[-1] != 'api'
  end

end

Late to the party, but something like this can be done:

class YourCustomStrategy
  def initialize(controller)
  end

  def handle_request
  end
end

And in your ApplicationController or where you want:

class ApplicationController < ActionController::Base
 protect_from_forgery with: YourCustomStrategy
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top