While I posted a form from iframe in safari, it gives Invalid Authenticity Token exception. If I try without iframe, then it works fine.
Why it is happening? How can I fix this?
The authenticity token is a mechanism that rails uses to protect users from CSRF attacks. Here is a good explanation, taken from Understanding the Rails Authenticity Token
When the user views a form to create, update, or destroy
a resource, the rails app would create a random authenticity_token,
store this token in the session, and place it in a hidden field in the
form. When the user submits the form, rails would look for the
authenticity_token, compare it to the one stored in the session, and
if they match the request is allowed to continue.
So basically, for any action that would modify your model rails wants to verify that it is a change originated by you.
Rails does that (through the use of form_for or form_tag helpers) by adding that secret authenticity token to the form with a html tag like this: <input name="authenticity_token" type="hidden" value="Som3Thin10ngAndUGly">
Back to your problem: I've never worked with iframes so I'm not sure what's happening, but my guess is that your iframe form is not passing the authencity_token. If this is the case the solution is simple, just add a hidden input like the one above and use the form_authenticity_token method to set its value.
Related
I got an ASP NET Core RazorPage having a button which asynchronously replaces a part of the given HTML using an AJAX request.
Besides some text content it renders another button which is intended to post back the side when clicked. It is surrounded by a form element.
However, clicking the button I receive an HTTP 400 with the information "This page isn't working" (Chrome). Other browsers like Firefox return an HTTP 400 as well.
The relevant HTML with the button which has been created by the AJAX call is the one below:
<form method="post">
<button class="btnIcon" title="Todos" id="btnTodos" formaction="PersonManagement/Parts/MyPageName?handler=PerformTodos">Execute action</button>
</form>
As the url exists (I doublechecked it using the browser with a simple GET) I wonder whether the issue could be due to some security settings along with the browser or is there anything I am perhaps missing out here?
Thank you for any hint
Two things here first add this attribute to your form asp-antiforgery="true", then send it's value to the server in your AJAX post request.
jQuery magic starts here :)
token: $('[name=__RequestVerificationToken]').val(),
Antiforgery is ON by default since .net core 2.0 (as far as I remember), so if you do AJAX post you need to send the antiforgery token with each request.
Let us know if it helps. Spread knowledge don't hide it just for yourself :P
Finally I came across a very interesting article from Matthew Jones at https://exceptionnotfound.net/using-anti-forgery-tokens-in-asp-net-core-razor-pages/ about Anti-Forgery Tokens in Razor pages. Worth reading, indeed.
However, independently from that article what solved my issue was simply not to add the <form .. element at the client-side, but already at the server-side. As there is no need for me to explicitly adding it at the client-side, but only the button itself, this is a solution for me which works properly.
A brief summary of my scenario now:
There is a Razor Page containing usual cshtml content along with a <form method="post"..
Some anchor elements also are included, one is triggering a JQuery AJAX call to the server
The JQuery call comes back from the server with some additional HTML including the post button which which I add to the existing HTML.
The button is being rendered inside the now already existing
Clicking the button causes the page to post back in the wanted manner and executes the handler as intended.
Thanks again Stoyan for your input and help with that.
I have a form that I give to my customers to put on THEIR website. The form allows one of THEIR customers to request an appointment through my scheduling software.
My scheduling software has a controller action that creates the customer record and saves the work request to MY customer's environment (inside my software).
When the record is saved, I would like to pop and alert or do a flash[:success]-esque notice to THEIR customer that the requested was successfully submitted.
I DO NOT WANT TO USE AJAX FOR THIS.
Is there a way to put a on MY customer's page and have the Rails redirect_to :back, :notice => "Request submitted successfully" function properly?
I can't give my customer a form.html.erb file or snippet. It needs to be dumb, pure, basic HTML that will work under any webpage. Lot's of restrictions for this.
I understand how I might do this with AJAX - I want to know if there is a way to do it without AJAX.
I did NOT find any solution to using :notice the way that I want, but one work-around I did find was to put a hidden field in my form, manually set the callback URL, and pass that as the redirect like so:
<input type="hidden" name="redirect" value="CALLBACK URL HERE">
In the controller after I submit my data I do:
redirect_to params[:redirect]
Which works a treat because you can put a nice landing page instead of just having a JavaScript alert saying "Success" or whatever
The error in the title is visible only in firebug. Everything from where I put the recaptcha element on down, is not shown on the page, though is present in the page-source (Mozilla and Opera) - though no error is shown in firebug.
So far, based on others solutions, I have tried reversing the keys (public and private, though they are clearly identified), generating a global-key-pair and using those, and even hard-coding the values into the recaptcha.rb initializer file versus using system-vars. No luck in any cases in dev or production. Also tried suppressing the 'noscript' part, with no change.
The Gem-Generated Page Source reads:
<script type="text/javascript" src="//www.google.com/recaptcha/api/challenge?k=[" mypublickeyhere", "myprivatekeyhere", false]&lang="></script>
<noscript>
<iframe src="//www.google.com/recaptcha/api/noscript?k=["mypublickeyhere", "myprivatekeyhere", false]" height="300" width="500" style="border:none;"></iframe><br/>
<textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
<input type="hidden" name="recaptcha_response_field" value="manual_challenge"/></noscript>
Why is my private key visible in the page-source? All that code comes from putting this in my view:
<%= recaptcha_tags %>
Edit: Made some progress, many hours in, by force-feeding the keys in the form and controller with:
<%= recaptcha_tags :public_key => 'mypublickeyhere' %>
and
if ( verify_recaptcha :private_key => 'myprivatekeyhere' )
Which gets the recaptcha to show up on the form, and keeps my private-key from being spammed to the page-code by the plugin as it does in 'default' mode.
Unfortunately, even if captcha is entered correctly, we get a NEW Error, "invalid-request-cookie".
Is there a single example of using this plugin in Rails 3, with full working form and controller code?
More Info for other sufferers:
Google Says this error means: "The challenge parameter of the verify script was incorrect."
On another page, if you search for "challenge parameter," to find out whatever that is, Google says: "recaptcha_challenge_field is a hidden field that describes the CAPTCHA which the user is solving. It corresponds to the 'challenge' parameter required by the reCAPTCHA verification API."
So why is the plugin not providing the correct challenge parameter as it should? Perhaps I need to pass something somewhere - but what and where? Again, a simple example would be great.
0.0. Setting the Variables - an aside:
Use ENV['key'] to keep your keys out of the codebase (though you can hardcode them in /config/environments/development.rb and then not include this file on your production server (for Heroku, add to gitignore in your push folder).
I added this to my development.rb file
# Set variables for Recaptcha on Localhost
ENV['RECAPTCHA_PUBLIC_KEY'] = 'mypublickeyhere'
ENV['RECAPTCHA_PRIVATE_KEY'] = 'myprivatekeyhere'
You will put your real key values in place of mybpublickeyhere and myprivatekeyhere.
You could also set ENV variables on your dev-machine. I prefer not to add that clutter, as this machine is used to develop many sites at once.
If deploying to Heroku, learn how to set these ENV variables here:
http://devcenter.heroku.com/articles/config-vars
1.0 Get a set of global-keys, not tied to any particular domain, and use these for testing. After eliminating that potential problem, when all is working, put in your domain-specific keys, on your production machine, and re-test.
2.0 Don't use the 'default' method. From what I can tell, it simply does not work - maybe it once did and Google changed something - I don't know, but it may/will give you the dreaded "Input error: k: Format of site key was invalid" AND will reveal your private key to anyone who views the page-source.
The solution is to force-feed the keys into the form and controller. So, in your form this will look like:
<%= recaptcha_tags :public_key => ENV['RECAPTCHA_PUBLIC_KEY'] %>
3.0 In your controller you will test for true; but again, force-feed the private key like this:
if ( verify_recaptcha :private_key => ENV['RECAPTCHA_PRIVATE_KEY'] )
... your success code here
else
... your fail code here
end
4.0 Placement of the tag in the form is important. The Devise docs refer to this gem, and provide actual example code of using this gem:
http://github.com/plataformatec/devise/wiki/How-To:-Use-Recaptcha-with-Devise
They say to put the recaptcha_tags immediately above the submit button code. This is important. I had to put it within:
<div class="form-actions">
... along with the button
Other sources report that surrounding HTML can break things in mysterious ways, so you may have to experiment for awhile (hope you don't have deadlines, or anything). These 'placement' issues were the culprit with the 'invalid-request-cookie' error I received.
I hope these guidelines shorten your development time.
The problem is that i have a remote form that, based on condition, id like to convert to a non-remote form (using UJS), and then submit.
note the form has a file upload.
Here's the details: I have initially rendered the remote form using
= form_for #myobj, :url => {:action=>"remoteAction", :controller=>"myobjects"}, :remote => true do |f|
... (f.fields....)
which produces the HTML:
<form id="new_myobj" class="new_myobj" method="post" accept-charset="UTF-8" data-remote="true" action="/remoteAction">
when i click submit, as expected, the form is submitted 'AS JS'.
in the controller action, i am doing some validation of the fields inside the submitted form.
If all the validations pass, i execute the following .js.haml template:
$('form#new_myobj').removeAttr("data-remote");
$('form#new_myobj').attr('enctype', 'multipart/form-data');
$('form#new_myobj').attr('action', '/myobjects/regularAction');
which successfully changes the HTML on the page (witnessed via Firebug) to:
<form id="new_myobj" class="new_myobj" method="post" accept-charset="UTF-8" enctype="multipart/form-data" action="/myobjects/regularAction">
since the form contains an f.file_field, i have to submit as multipart so the image can be uploaded, and i cannot submit 'AS JS'
now, when i click submit, the controller action 'regularAction' is indeed called, but its still 'AS JS'
the question is, what else do i need to change in the HTML so the form can be submitted non-xhr? is it related to the headers?
jQuery is a bit tricky with the data attributes since it both
reads the HTML5 data tags as well as its own storage bound to the
DOM element, that is also called data. When writing to an attribute
that value gets copied into jQuerys own data storage (presumably
when data("remote") is being called).
However, this only happens
if jQuery’s data is empty for that name. Thus setting the attribute will only work once, after that the "cached" value is being used
even if the attribute changes. In order to really get rid of the
value, we need to remove the attribute and jQuerys own storage
method in that order. The reason is that there’s a high-level
(element.removeData(…)) function and a low level one (jQuery.
removeData(element, …)). The former re-reads the HTML5 data
attribute and stores it in jQuery’s own storage. Using the rather
unusual low level function obviously works as well.
Also, we do really need to remove the attribute -- setting it to
false is not enough since Rails only checks if form.data('remote')
is not undefined (look for it in jquery_ujs.js).
TL;DR:
attr("data-remote") != data("remote")
These two lines make a form non-remote (again). Order matters.
$("form").removeAttr("data-remote");
$("form").removeData("remote");
It’s documented, if you actually know what you’re looking for:
http://api.jquery.com/jQuery.data/ (low level function)
http://blog.madebydna.com/all/code/2011/12/05/ajax-in-rails-3.html
StackOverflow doesn’t allow me to post more than two links, but you can guess the removeData one. The high-level functions are linked from the low level ones.
Avoiding the token authenticity error in Rails 4+:
As Stan commented below, just doing the above will fail with an InvalidAuthenticityToken error. The workaround is easy though, see here for details: https://stackoverflow.com/a/19858504/1684530
The problem is that your approach to disable the Ajax submission isn't quite correct. You need to unbind the JavaScript events that have already been added by rails.js (Rails UJS adapter) to the form.
You can do that by:
$('form#new_myobj').unbind() to unbind all events attached to the form. You also need to $('form#new_myobj').removeAttr('data-remote') and $('form#new_myobj').removeAttr('data-type') to remove data-remote and data-type attributes (if existent).
Suppose I'm building a login system. The user enters a username and password into a field and it is sent via HTTPS to the server, which validates the login before the page loads. If a bad password is sent, the login obviously fails immediately, but one would want the error message to be displayed later in the page, near the login box.
The obvious solution is to set a global flag and have the login box check it and add the error message if necessary, but my understanding is that global variables are best avoided. Is there another straightforward method of achieving this functionality?
For a non-AJAX login page, it is common practice to redirect the user browser to the login page with an extra query parameter in the url, In pseudo-code, here is the login validation controller code segment:
success = checkLogin(username,password)
if (success == false)
redirect('http://example.com/login?failedlogin=true')
The login page controller would be responsible for detecting this query param and telling the view code to display a failure message. I don't believe the term 'global flag' applies to this practice so it should meet your requirements.
If the login page uses Ajax, the Javascript on the login page takes the results of the AJAX call and updates the appropriate DOM elements with the failure message.