Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing Authenticity Token in Remote Forms w/ File Upload Field(s) #22807

Closed
mvastola opened this issue Dec 27, 2015 · 20 comments
Closed

Missing Authenticity Token in Remote Forms w/ File Upload Field(s) #22807

mvastola opened this issue Dec 27, 2015 · 20 comments

Comments

@mvastola
Copy link
Contributor

This is more-or-less a cross-post of rails/jquery-ujs#451.

This issue deals with form_for tags containing one or more file_fields 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 to true; 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 POSTed 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_fors 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.)

@rails-bot
Copy link

This issue has been automatically marked as stale because it has not been commented on for at least
three months.

The resources of the Rails team are limited, and so we are asking for your help.

If you can still reproduce this error on the 5-0-stable branch or on master,
please reply with all of the information you have about it in order to keep the issue open.

Thank you for all your contributions.

@aguynamedben
Copy link
Contributor

aguynamedben commented Nov 4, 2016

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: rails/jquery-ujs#451 (comment)

An easy workaround is to add authenticity_token: true to your form_for:

<%= form_for @user, remote: true, authenticity_token: true do |f| %>
  <%= f.file_field :image %>
<% end %>

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:

Use only if you need to pass custom authenticity token string, or to not add authenticity_token field at all (by passing false).

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 on form_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.

@aguynamedben
Copy link
Contributor

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 using remote: 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 the form_for also has authenticity_token: false.

I'm happy to writeup a PR with doc updates. Just trying to help. Thanks for your work on Rails, I <3 it.

@iamvery
Copy link
Contributor

iamvery commented Feb 22, 2017

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.

@onomojo
Copy link

onomojo commented Mar 20, 2017

This is still happening in Rails 5.0.1

@rails-bot
Copy link

This issue has been automatically closed because of inactivity.

If you can still reproduce this error on the 5-0-stable branch or on master,
please reply with all of the information you have about it in order to keep the issue open.

Thank you for all your contributions.

@mvastola
Copy link
Contributor Author

@rails-bot, please re-open. As @onomojo stated just yesterday, he can still reproduce this issue. (I personally haven't tried.) I don't know how you're configured, but I define inactivity as a lack of activity. A comment = activity. :-
Please re-open.

@rafaelfranca
Copy link
Member

should a hidden input element always be inserted by actionview in remote form_fors in which a file_field is present (regardless of config.action_view.embed_authenticity_token_in_remote_forms)?

Let's go with this solution

@rafaelfranca
Copy link
Member

Hmm. Better yet, we should go with the solution commented rails/jquery-ujs#451 (comment). Could someone work on it?

@aguynamedben
Copy link
Contributor

@rafaelfranca Okay. I'm in the middle of a huge sprint right now. What timeframe would be ideal to complete the work? I could maybe do it in 2-4 weeks from now. Thanks for your feedback on this, I'd like to give back by doing this.

@rafaelfranca
Copy link
Member

We waited more than three moths already so I think 2-4 weeks is fine 😄.

@rails-bot rails-bot bot added the stale label Jun 27, 2017
@rails-bot
Copy link

rails-bot bot commented Jun 27, 2017

This issue has been automatically marked as stale because it has not been commented on for at least three months.
The resources of the Rails team are limited, and so we are asking for your help.
If you can still reproduce this error on the 5-1-stable branch or on master, please reply with all of the information you have about it in order to keep the issue open.
Thank you for all your contributions.

@rails-bot rails-bot bot closed this as completed Jul 4, 2017
@onomojo
Copy link

onomojo commented Jul 5, 2017

That's certainly one way to fix the bug. Pretend it doesn't exist.

@rails-bot rails-bot bot removed the stale label Jul 5, 2017
@mvastola
Copy link
Contributor Author

mvastola commented Jul 5, 2017

Go drunk, @rails-bot[bot], you're home.

@chamnap
Copy link

chamnap commented Jan 15, 2018

I can confirm that this bug still exists on Rails 5.0.6.

@NicosKaralis
Copy link

I can confirm, this still happens on rails 5.2.1

@rassas
Copy link

rassas commented Nov 30, 2018

I've got the same issue with rails 5.2.1.
The problem is that when setting remote: true in form_for, csrf_token won't be sent with file upload.
In order to resolve this you should:

  1. set authenticity_token: true
<%= form_for @user, remote: true, authenticity_token: true do |f| %>
  <%= f.file_field :image %>
<% end %>
  1. Enable the direct upload of the upload module that you are using, for exemple if you are using ActiveStorage so you should work with ActiveStorage npm module to activate the direct upload.

@jeremywadsack
Copy link
Contributor

jeremywadsack commented Dec 23, 2019

This same thing happened moving from jquery_ujs to rails_ujs in Rails 5.2.1.

@artur79
Copy link

artur79 commented Sep 8, 2021

Don't need to set verbosely:
authenticity_token: true
if you set in pack/application.js:
require('@rails/ujs').start()

yarn install @rails/ujs before, of course

@daiyan71
Copy link

daiyan71 commented May 30, 2024

$(document).on('click', '#submit-button', function(event) {
                event.preventDefault();
                submitAjaxForm($('#your-form'));
            });

function submitAjaxForm(form) {
    $.ajax({
        url: form.attr('action'),
        type: form.attr('method'),
        data: new FormData(form[0]),
        processData: false,
        contentType: false,
    });
}

This is how I solved the issue. Remove remote: true from the form and then submit the form using jquery.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests