This article was written when one of our Rails applications was upgraded to Rails 5.2.
After upgrading our application to Rails 5.2, InvalidAuthenticityToken errors are found in our public APIs. The error is caused by CSRF protection is enabled by default since Rails 5.2 while it is not required in public APIs. I updated the config to disable the default behavior, but it doesn't take effect.
# config/initializers/new_framework_defaults_5_2.rb Rails.application.config.action_controller.default_protect_from_forgery = false
The problem
According to the source code, the config is used to toggle CSRF protection after ActionController::Base is loaded:
initializer "action_controller.request_forgery_protection" do |app| ActiveSupport.on_load(:action_controller_base) do puts "default_protect_from_forgery: #{app.config.action_controller.default_protect_from_forgery}" # Add this line to print the config value if app.config.action_controller.default_protect_from_forgery protect_from_forgery with: :exception end end end
I use this gist to debug how configurations are loaded:
before_configuration
application.rb loaded
before_initialize
default_protect_from_forgery is true, expected: false
config/initializers loaded
ActiveSupport.on_load(:before_initialize) runs at the end of before_initialize
after_initialize
default_protect_from_forgery is false, expected: false
ActiveSupport.on_load(:after_initialize) runs at the end of after_initialize
The red-colored line shows the default_protect_from_forgery is true while it is expected to be false. So the problem is ActionController::Base is loaded before loading config/initializers.
Investigation
I commented all gems except the rails gem, then uncommented them one by one and debug configuration loading with rails c to see the prints like above, finally found it is caused by the wechat gem:
https://github.com/Eric-Guo/wechat/blob/v0.12.2/lib/wechat.rb#L10
https://github.com/Eric-Guo/wechat/blob/v0.12.2/lib/action_controller/wechat_responder.rb#L68
https://github.com/Eric-Guo/wechat/blob/v0.12.2/lib/wechat.rb#L10
https://github.com/Eric-Guo/wechat/blob/v0.12.2/lib/action_controller/wechat_responder.rb#L68
Solution
The problem can be fixed if:
- gems load files with Zeitwerk (e.g. https://github.com/Eric-Guo/wechat/pull/293).
- gems load files in after_initialize with Railtie.
I created a repo for showing the problem, you can see two commits:
- Yejun