Rails 5 API protect_from_forgery

I have a Rails 5 API application ( ApplicationController < ActionController::API ). The need came to add a simple GUI form for one endpoint of this API.

I originally got ActionView::Template::Error undefined method protect_against_forgery? when trying to process the form. I added the include ActionController::RequestForgeryProtection and protect_from_forgery with:exception to this endpoint. What solved this problem was as expected.

However, when I try to submit this form, I get: 422 Unprocessable Entity ActionController::InvalidAuthenticityToken . I added <%= csrf_meta_tags %> and confirmed that meta: csrf-param and meta: csrf-token are present in my headers and that authenticity_token present in my form. (The tokens themselves are different from each other.)

I tried protect_from_forgery prepend: true, with:exception , no effect. I can "fix" this problem by commenting: protect_from_forgery with:exception . But I understand that this disables CSRF protection in my form. (I want CSRF protection.)

What am I missing?

UPDATE:

To make this clear, 99% of this application is a pure JSON RESTful API. The need came to add one HTML view and form to this application. Therefore, for one controller, I want to enable full CSRF protection. The rest of the application does not need CSRF and can remain unchanged.

UPDATE 2:

I simply compared the page source of this form of the HTML application and the title with another regular Rails 5 application that I wrote. authenticity_token in the header and authenticity_token in the form are the same. I have a problem in the API application, they are different. Maybe something?

UPDATE 3:

Well, that is not a problem. However, in a further comparison between running and non-working applications, I noticed that there is nothing in Network> Cookies. I see a bunch of things like _my_app-session in the cookies of the working application.

+13
security ruby-on-rails ruby-on-rails-5 csrf
source share
4 answers

Here's the problem: Rails 5, when in API mode, does not logically enable Cookie middleware. Without it, there is no key session stored in the Cookie that will be used to validate the token that I submitted with my form.

Somewhat ridiculously, changing things in config/initializers/session_store.rb had no effect.

In the end, I found the answer to this problem: Adding the cookie session store back to the Rails API application , which brought me here: https://github.com/rails/rails/pull/28009/files , which indicated exactly those lines which I needed to add to application.rb to get working Cookies back:

 config.session_store :cookie_store, key: "_YOUR_APP_session_#{Rails.env}" config.middleware.use ActionDispatch::Cookies # Required for all session management config.middleware.use ActionDispatch::Session::CookieStore, config.session_options 

These three lines are combined with:

 class FooController < ApplicationController include ActionController::RequestForgeryProtection protect_from_forgery with: :exception, unless: -> { request.format.json? } ... 

And, of course, the form created through the right helpers:

 form_tag(FOO_CREATE_path, method: :post) ... 

Got a CSRF protected form in the middle of my Rails API application.

+13
source share

If you use the Rails 5 API mode, you are not using protect_from_forgery or include <%= csrf_meta_tags %> in any view, since your API is stateless. If you are going to use full Rails (not API mode), while ALSO uses it as a REST API for other applications / clients, you can do something like this:

 protect_from_forgery unless: -> { request.format.json? } 

So that protect_from_forgery called when necessary. But I see the ActionController::API in your code, so it looks like you are using API mode, in which case you completely remove this method from your application controller.

+8
source share

There is no need to protect_from_forgery for AJAX and apis calls.

If you want to disable it for some actions, then

 protect_from_forgery except: ['action_name'] 
+3
source share
 class Api::ApiController < ApplicationController skip_before_action :verify_authenticity_token end 

Use as above with rails 5

0
source share

All Articles