rails: Missing Authenticity Token in Remote Forms w/ File Upload Field(s)
This is more-or-less a cross-post of https://github.com/rails/jquery-ujs/issues/451.
This issue deals with form_for
tags containing one or more file_field
s where :remote => true
.
config.action_view.embed_authenticity_token_in_remote_forms
defaults to false
to facilitate fragment caching (which makes total sense), but the jquery-ujs
gem is currently coded to permit (without alteration – meaning it will submit as non-ajax) the submit event of any form if:
- the
data-remote
attribute in<form>
tag is set totrue
; and, - the
<form>
tag contains one or more<input type="file" />
children; and, - any
<input type="file" />
tag has a file selected for upload
(Note that, by design, jquery-ujs
does not provide a method to submit file uploads via AJAX. It only provides a non-AJAX fallback. To submit a remote form with file uploads via AJAX, one must include javascript that catches the submit action before jquery-ujs
does.)
Long story short, there is an issue in that jquery-ujs
cannot currently fulfill its role to provide a non-AJAX fallback since there is no hidden input element in the <form>
provided by actionview
(and jquery-ujs
is not using the page’s meta tags as it does for remote requests) containing an authenticity token, so none is POST
ed an InvalidAuthenticityToken
is raised in response to the form submission.
The question du jour is where this bug should be fixed: should a hidden input element always be inserted by actionview
in remote form_for
s in which a file_field
is present (regardless of config.action_view.embed_authenticity_token_in_remote_forms
)? (This is the solution suggested by the jquery-ujs
owner.) Alternatively, should jquery-ujs
create a hidden authenticity_token
input tag inside the <form>
dynamically (with the value from the page’s meta tags) immediately before it is about to fulfill its fallback role (my inclination).
Please advise/discuss/etc. (I’m happy to contribute a PR resolving the issue in whatever manner is mutually agreeable, but obviously either rails
or jquery-ujs
must be willing to merge it.)
About this issue
- Original URL
- State: closed
- Created 9 years ago
- Reactions: 3
- Comments: 19 (10 by maintainers)
Wow, I’ve been tracking this down for hours, and it was painful. It’s exactly as @mvastola describes it—CSRF fails when a form (1) has file inputs and (2) has remote: true.
My instinct is that this should be fixed in jquery-ujs. I documented my opinion on a fix here: https://github.com/rails/jquery-ujs/issues/451#issuecomment-258547646
An easy workaround is to add
authenticity_token: true
to yourform_for
:That workaround is easy if you’re not fragment caching forms, and ignores the documentation’s statement that you should only use the
:authenticity_token
to customize or hide the field:Perhaps another way to “fix” this is to always include the authenticity token, and to instruct users who care about fragment caching to use
authenticity_token: false
onform_for
. They’re already having to perform nuanced coding around where to place cache statements, it seems reasonable that they should also have to remove CSRF tokens before caching.This issue has been automatically closed because of inactivity.
If you can still reproduce this error on the
5-0-stable
branch or onmaster
, please reply with all of the information you have about it in order to keep the issue open.Thank you for all your contributions.
I can confirm, this still happens on rails 5.2.1
I can confirm that this bug still exists on Rails 5.0.6.
I’ve got the same issue with rails 5.2.1. The problem is that when setting
remote: true
inform_for
, csrf_token won’t be sent with file upload. In order to resolve this you should:authenticity_token: true
direct upload
of the upload module that you are using, for exemple if you are usingActiveStorage
so you should work withActiveStorage
npm module to activate the direct upload.Go drunk, @rails-bot[bot], you’re home.
That’s certainly one way to fix the bug. Pretend it doesn’t exist.
One more thing, then I will let wiser people advise…
While debugging this behavior in my app, the most confusing thing going on was that setting
config.action_controller.per_form_csrf_tokens = true
didn’t insert an HTML element in my forms when usingremote: true
. For a while, I believed that config option was plain not working, deprecated, or who knows what.It seems like the nuance that per-form CSRF tokens are never inserted on forms with
remote: true
should be documented here: http://edgeguides.rubyonrails.org/configuring.html Or like I advised, the default behavior should be always insert them unless theform_for
also hasauthenticity_token: false
.I’m happy to writeup a PR with doc updates. Just trying to help. Thanks for your work on Rails, I ❤️ it.
I just wanted to drop a brief note here and mention that I just ran into this issue. I noticed the ActionController::InvalidAuthenticityToken error when submitting a
remote: true
form with JS turned off. I expected it to fallback to a regular form submission.I like @aguynamedben’s suggestion to include the token by default in order to support fallback.