Submitting a form requires a token which the receiving server has named authenticity_token and is expecting a string that is known only to posting and receiving servers:
<input id="authenticity_token" name="authenticity_token" type="hidden" value="ac513de198a9f0536df5003fb5ba3122d1ee53d5" />
that value renders if I call an instance or global variable. However, even though the controller is set with the following filter:
skip_before_action :verify_authenticity_token, only: [:reservation]
whether I try
<%= form_tag('https://test.webten.net/form') do %>
<%= hidden_field_tag :authenticity_token, #ppayment.authenticity_token, id: "authenticity_token" %>
or
<%= tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: #ppayment.authenticity_token) %>
or
<input id="" name="authenticity_token" type="hidden" value="ac513de198a9f0536df5003fb5ba3122d1ee53d5" />
Rails ends up squashing each value with its own session setting value and rendering:
<input type="hidden" name="authenticity_token" id="authenticity_token" value="ydi5En1ywUkN5VsYIBXu6JTbQmXtwxNhpKlyjbbLi3RdvCc+A59EdDZvroGsGFplAAE5ATLcSqw25LVQkyPtKw==">
<input type="hidden" name="authenticity_token" value="ydi5En1ywUkN5VsYIBXu6JTbQmXtwxNhpKlyjbbLi3RdvCc+A59EdDZvroGsGFplAAE5ATLcSqw25LVQkyPtKw==">
<input id="authenticity_token" name="authenticity_token" type="hidden" value="ydi5En1ywUkN5VsYIBXu6JTbQmXtwxNhpKlyjbbLi3RdvCc+A59EdDZvroGsGFplAAE5ATLcSqw25LVQkyPtKw==">
How can rails default behaviour be overridden in this controller action?
You have to override the :authenticity_token argument in the form_tag call to prevent Rails from adding the session's authenticity token by default:
<%= form_tag('https://test.webten.net/form', authenticity_token: 'ac513de198a9f0536df5003fb5ba3122d1ee53d5') do %>
or:
<%= form_tag('https://test.webten.net/form', authenticity_token: false) do %>
<%= hidden_field_tag 'authenticity_token', 'ac513de198a9f0536df5003fb5ba3122d1ee53d5' %>
If that doesn't work, you probably have some JS that overwrites the authenticity token you provided from the one in the META tag. Search for 'csrf-token' in your JS.
See http://guides.rubyonrails.org/form_helpers.html#forms-to-external-resources for more information.
[this answer is complementary to the preceeding one. It solves the issue, but in an inelegant manner]
Various possible avenues of solution appear incomplete / off the mark
form_authenticity_tokenis a view helper that returns the current
session's authenticity token. Too late to act upon that as the
layout may already invoke it
most voted response has a per action method
protect_from_forgery for ignoring it for internal purposes. Still
is too late as session already has issued it
a skip before action is just like the previous solution, too
late, session token is issued
The rails guide is incorrect in stating: authenticity_token:
'external_token'
Aaron Breckenridge's answer provide a clue: "you probably have some JS that overwrites the authenticity token ", supported by this posting. Thus one could search the content of app directory and still not find a JS handler... when jquery-ujs, for example, is gem installed. Based on observed behaviour, it must be getting fired up every time it encounters name='authenticity-token' and sets the value based on the META tag (logical. why have 2 different tokens on same page...) - doing its job properly!
Literally, that token must not be generated upstream. So modifying the layout's header section:
<% unless request.path == "/controller/action" %>
<%= csrf_meta_tags %>
<% end %>
is a working solution. This solution gets pollution-prone for multiple paths. Another requires multiple layout handling... thus, file under 'kludgey'. (willing to entertain better one!)
Related
I take courses on rails 5.x.x and when they used form they add a line for token authentication to protect their site, on the start of the form, like this :
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
But to be on the last version of rails i'm on 6.1.3 version and i don't see anything on the web about that.
So the question is : Did I still need to set this authenticity token anywhere ? if yes, where ? and if no, why ? If you have some links about that for rails 6 I don't say no. Thank's you.
No, you don't need to add it manually, Rails does it for you in each form.
<%= form_with do |form| %>
Form contents
<% end %>
generates
<form accept-charset="UTF-8" action="/" method="post">
<input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
Form contents
</form>
You'll notice that the HTML contains an input element with type
hidden. This input is important, because non-GET forms cannot be
successfully submitted without it. The hidden input element with the
name authenticity_token is a security feature of Rails called
cross-site request forgery protection, and form helpers generate it
for every non-GET form (provided that this security feature is
enabled). You can read more about this in the Securing Rails
Applications guide.
https://guides.rubyonrails.org/form_helpers.html
As per the rails docs, using the submit_tag helper will generate something like
submit_tag
# => <input name="commit" type="submit" value="Save changes" />
It includes the property name="commit"
Why does this get included? Is it some sort of standard? Trying to understand what "commit" means in this context
I noticed it gets included in the params inside the controller after hitting submit. Was this changed recently? I thought only url params and POST/PUT data get included?
#params
=> {"utf8"=>"✓", "authenticity_token"=>"4q3u+mfMi57YbchTAzaCI7WHmzfZJrLbTZ17aVwfq9uw8aMU1B3PBR13qfipgN6lbRoi0dywFU9i1AbJ0GP7UA==", "email"=>"foo#example.co", "password"=>"(REDACTED)", "dest"=>"", "commit"=>"Log in", "controller"=>"sessions", "action"=>"log_in"}
Thanks!
I believe this is just a rails thing. HTML5 standards allow you to provide a name=xxxx on your own, which you can also do with rails. In short, name="commit" helps in case you need to do something like this guy and makes sure something is passed in the parameters.
Here's my examples that produce the same result :
# `enterprise_registration` is an already created/saved object
form_for enterprise_registration, method: :put do |format|; format; end
form_for enterprise_registration, url: logo_url, method: :put do |format|; format; end
form_for enterprise_registration, url: logo_url, html: {method: :put} do |format|; format; end
This returns the form with the method attribute set to POST.
Why is that happening, do you think? And how can I make it a :put request?
Update
I now understand that Rails forms embed a hidden _method and set it to put, but my form is still getting delivered as a POST that is preventing my form from finding my matching PUT URL
form tag has only GET or POST method allowed. See also here for more explanation. Rails, however, has his own method to handle GET/POST/PUT/PATCH requests. If you would examine any of your form defined as either form_for or form_tag in Rails, you will notice that first element of the form is a hidden <div> which contain two hidden fields:
<div style="display:none">
<input name="utf8" type="hidden" value="✓"><input name="_method" type="hidden" value="patch">
<input name="authenticity_token" type="hidden" value="9i5eRhwhx4NhvSxqIJm6cv9x6NSlY82hpNpfrpk/I0c=">
</div>
First field called _method contains the form action which is the request type for controller.
Web browsers are actually only programmed to receive POST and GET requests (I'm not sure why). The way Rails mimics full REST (which includes put and delete) is include those in hidden fields. So technically it's sending a POST, but with the PUT attached sort of awkwardly in that hidden field.
I'm assuming that Rails is still doing that sort of conversion behind the scenes still.
On a particular page in my website, there are a variety of potential URL parameters:
http://www.example.com/my_webpage?at=2014-01-01&page=5
Now I want a simple way to add a parameter to that like this:
http://www.example.com/my_webpage?at=2014-01-01&page=5&records=100
So I tried the following HTML with embedded Ruby:
<form action="<%= request.original_url %>" method="get"># of records <input type="text" name="records"/></form>
The problem is the resulting page that opens is:
http://www.example.com/my_webpage?records=100
Essentially, the old parameters get wiped away. What's an easy way to retain them? I could loop through the params hash and add hidden_tags (I'd have to selectively exclude params not part of the request params), but I would expect with such a common use case scenario there's a better easier way.
While there isn't an easy Rails way of doing this automatically, Rails does provide access to the request parameters through the request.query_parameters hash. So I simply needed to add this in the form:
<% request.query_parameters.each do |key, val|%>
<input type="hidden" name="<%= key %>" value="<%= val %>"/>
<% end %>
After the user has logged in and their username authenticated and saved in session[:user_name], when submitting a form with method="post" (using standard html) all session data is cleared and user is routed back to the login page. This does not happen if the method is set to get.
Post works when I use the rails "form_tag" which generates these additional lines:
<div style="margin: 0pt; padding: 0pt; display: inline;">
<input type="hidden" value="✓" name="utf8">
<input type="hidden" value="a_bunch_of_gibberish" name="authenticity_token">
</div>
What is happening?
Are you using Rails 3.0.4? It sounds like it might be related to the CSRF fix.
If you need a fix specific to your needs, you can overwrite #handle_unverified_request in your controller. See https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/request_forgery_protection.rb.
Here's an example where this issue was with OmniAuth and OpenId. (See section 'CSRF Protection In Rails 3.0.4')
This issue can also happen if you're creating your own html form.
If you're getting redirected without knowing why, it's because of "protect_from_forgery" in your ApplicationController
In order to allow your form to post, add the following code to it (or use form_tag, explanation below):
<form ... >
...
<%= hidden_field_tag "authenticity_token", form_authenticity_token %>
...
</form>
The reason the "form_tag" works is because form_tag generates the hidden field for you =)