Submitting form through iframe: Rails 4 InvalidAuthenticityToken in Chrome, Safari - ruby-on-rails

I have a form on a page which other people can embed through an iframe on their sites. When submitting that form I get an ActionController::InvalidAuthenticityToken error, in Safari and also Chrome (with third-party cookies turned off) but not in Firefox.
My understanding was that I don't have to rely on third-party cookies being activated if I've got this in my header:
<head>
<%= csrf_meta_tags %>
</head>
But apparently that's not true...?
And I'm even seeing the token in my server log:
Parameters: {"utf8"=>"✓", "authenticity_token"=> "2ig3BPn9...
Can't verify CSRF token authenticity
So what's going on here?

From the ActionController::RequestForgeryProtection docs:
Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks by including a token in the rendered HTML for your application. This token is stored as a random string in the session, to which an attacker does not have access. When a request reaches your application, Rails verifies the received token with the token in the session.
So even if you embed the token with csrf_meta_tags you still need it also in the session (i.e. in the cookie). That's why with disabled third-party cookies it doesn't work in the iframe.

into form insert:
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>"/>
Ex:
<form name="as" target="open_here" method="get" action="material">
<input type="hidden" name="materialid" value="56"/>
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>"/>
<input type="submit" value="Submit open_here" />
</form>
or
<form name="unit" target="open_here" method="get" action="/redmine/issues_materials/material" >
<input type="hidden" id="material_id" name="materialid" value="" /><!-- valo value e inputado pelo script 2 document.getElementById('material_id').value = materialid; -->
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>"/> <!-- para funcionar tem que enviar essa chave junto-->
<input id="idunit" type="submit" value="open here submit" style="display: none;"/>
</form>
<iframe id="open_here" name="open_here" frameborder="0" scrolling="no" width="60" height="80"></iframe>

Related

OAuth authorization_code flow unauthorized unless done via Postman

I'm trying to get an application to work with the Microsoft Identity platform.
Sending an OAuth request via Postman appears to work, but when I try the authorizaton_code grant type myself, despite getting back an access token, the API that I'm trying to access always gives me an unauthorized error.
I'm sending a POST request to:
https://login.windows.net/<tenant_id>/oauth2/authorize?resource=<resource_uri>
With body data:
grant_type=authorization_code&
client_id=<client_id>&
redirect_uri=<redirect_uri>&
response_type=code
This gives me a redirect to my URI with the code in the querystring
I then request an access token with the code by sending a POST to:
https://login.windows.net/<tenant_id>/oauth2/token?resource=<resource_uri>
With the following content:
grant_type=authorization_code&
client_id=<client_id>&
redirect_uri=<redirect_uri>&
code=<the_code_from_the_redirect>&
client_secret=<client_secret>
This gives me back an access token:
{
"token_type": "Bearer",
"expires_in": "3599",
"ext_expires_in": "3599",
"expires_on": "1557783183",
"not_before": "1557779283",
"resource": "00000002-0000-0000-c000-000000000000",
"access_token": "<access_token_here>",
"refresh_token": "<refresh_token>",
"id_token": "<id_token>"
}
But this token doesn't work when calling the resource:
{
"error": {
"code": "Unauthorized",
"message": "The credentials provided are incorrect"
}
}
Doing the same using the Get New Access Token functionality in Postman seems to create the same post request in the Postman console (albeit with a different code, but it has to get a new code because I've already redeemed the other one and it knows nothing about it right?) but the access token that it returns works:
{
"error": {
"code": "NoLicense",
"message": "User has no license"
}
}
(Ignore the fact that it's an error - the user has no licence for the application I'm trying to query but that's ok)
Am I doing something fundamentally wrong? From what I can see, I'm following the OAuth flow correctly.
Figured this out - it's because I was passing the resource as part of the querystring and not part of the form.
When the identity platform generates the redirect/callback it only appears to include either the querystring elements when doing a GET or the form elements when doing a POST.
You can see that in the below:
<html>
<head>
<title>Continue</title>
</head>
<body>
<form method="POST" name="hiddenform" action="https://login.microsoftonline.com/<tenant_id>/oauth2/authorize">
<input type="hidden" name="grant_type" value="authorization_code" />
<input type="hidden" name="client_id" value="<client_id>" />
<input type="hidden" name="redirect_uri" value="https://businesscentral.dynamics.com" />
<input type="hidden" name="response_type" value="code" />
<input type="hidden" name="scope" value="" />
<noscript>
<p>Script is disabled. Click Submit to continue</p>
<input type="submit" value="Submit" />
</noscript>
</form>
<script language="javascript">window.setTimeout('document.forms[0].submit()', 0);</script>
</body>
</html>
After adding resource to the form data instead of the URL I got a slightly different redirect:
<html>
<head>
<title>Continue</title>
</head>
<body>
<form method="POST" name="hiddenform" action="https://login.microsoftonline.com/<tenant_id>/oauth2/authorize">
<input type="hidden" name="grant_type" value="authorization_code" />
<input type="hidden" name="client_id" value="<client_id>" />
<input type="hidden" name="redirect_uri" value="https://businesscentral.dynamics.com" />
<input type="hidden" name="response_type" value="code" />
<input type="hidden" name="scope" value="" />
<input type="hidden" name="resource" value="<resource_id>" />
<noscript>
<p>Script is disabled. Click Submit to continue</p>
<input type="submit" value="Submit" />
</noscript>
</form>
<script language="javascript">window.setTimeout('document.forms[0].submit()', 0);</script>
</body>
</html>
Which generated me the correct access token!

How do I setup Auto Return URL for a PayPal Buy Button?

I am using the html code for a simple Paypal buy button but was wondering if it's possible to setup an auto return url without having to do it through my own Paypal account itself?
I can't do it that way because it is a localhost website (cannot validate a real website) and I'm not creating a button through my selling tools.
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<!-- Identify your business so that you can collect the payments. -->
<input type="hidden" name="business" value="email#email.com">
<!-- Specify a Buy Now button. -->
<input type="hidden" name="cmd" value="_xclick">
<!-- Specify details about the item that buyers will purchase. -->
<input type="hidden" name="item_name" value="Demo">
<input type="hidden" name="amount" value="0.01">
<input type="hidden" name="currency_code" value="GBP">
<input type="hidden" name="return" value="~/">
<!-- Display the payment button. -->
<input type="image" name="submit" border="0" src="https://www.paypal.com/en_US/i/btn/btn_buynow_LG.gif" alt="PayPal - The safer, easier way to pay online">
<img alt="" border="0" width="1" height="1" src="https://www.paypal.com/en_US/i/scr/pixel.gif">
</form>
Yes you can set your own return URL. HTML Special Variables. You can add this code to your form.
<input type="hidden" name="return" value="http://localhost" />
In your html you are defining a relative path to your home directory but you will need an HTTP or HTTPS url for it to work. This is because the page that the paypal checkout is on is not in your root directory, so it needs a specific location.

How to return with POST after PayPal billing?

I created the next form:
<form name="_xclick" action="https://www.sandbox.paypal.com/webscr" method="post">
<input type="hidden" name="cmd" value="_xclick">
<input type="hidden" name="business" value="team#team.com">
<input type="hidden" name="currency_code" value="USD">
<input type="hidden" name="item_name" value="Subscription">
<input type="hidden" name="return" value="http://www.my_website.com/<%= params[:locale] %>/success">
<input type="hidden" name="amount" value="1">
<input type="image" src="http://www.paypal.com/en_US/i/btn/btn_paynow_LG.gif" border="0" name="submit" alt="Make payments with PayPal - it's fast, free and secure!">
</form>
But it redirects to my website via GET request. How can I redirect to my website via POST request after payment?
When I create my button in PayPal button factory:
<form action="https://www.sandbox.paypal.com/webscr" method="post" target="_top">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="SOME_VALUE">
<input type="image" src="https://www.paypalobjects.com/en_US/IL/i/btn/btn_buynowCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
</form>
When I click on the PayPal button, I have set up success and cancel redirects when I click on the PayPal button, but the following error is displayed:
PayPal cannot process this transaction because of a problem with the seller's website. Please contact the seller directly to resolve this problem.
So, how can I change my first code that it will perform a POST redirect after billing?
Can you please check "https://developer.paypal.com/docs/classic/paypal-payments-standard/integration-guide/Appx_websitestandard_htmlvariables/"
and "rm" variable for solutions. And also set the hidden fields values for the paypal payment submissions.

non-customized devise reset password button is doing absolutely nothing

I have devise login and logout working, been using it for a while now.
I have not copied devise's password controller or password views to my application, just using the out of the box form for password/new. After entering the email of a valid user, when I click the "send me password reset instructions" button, nothing happens. I checked my logs and there is no activity post-click. I've read through devise documentation but it doesn't seem like there is any special setup steps to get the reset password functionality working. What could be wrong?
For more context, I have followed devise's instructions for requiring admin to activate account before sign in. I have pulled the registrations and sessions controllers into my app for minimal customization, though that doesn't seem like it should matter. I'm using rails 4.2.0.rc2 and devise 3.4.1
Here's the rendered html for the form:
<h2>Forgot your password?</h2>
<form class="new_user" id="new_user" action="/users/password" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓" /><input type="hidden" name="authenticity_token" value="AE+YY8afzRavKa1ziDFZ5Z6FqFYQF1vI+zvc701JGqfg5k+ztvMIKHDX4Wdfr+KOp9yEcRBTsk4jfonpzUeP5w==" />
<div class="field">
<label for="user_email">Email</label><br />
<input autofocus="autofocus" type="email" value="" name="user[email]" id="user_email" />
</div>
<div class="actions">
<input type="submit" name="commit" value="Send me reset password instructions" />
</div>
</form>
your javascript is preventing the form from being submitted unless a terms-of-service field is checked.
from your application.js file:
if $('#user_terms').is(':checked')
else
e.preventDefault()
$('#terms-of-service-error').modal()
return

how to generate AuthenticityToken on rails

I build the form tag by myself and when I post the form to server it give me a InvalidAuthenticityToken error, so I want to know how to add it in my own in current situation:
<form accept-charset="UTF-8" action="/crops/update" method="post">
<input id="crop_x" name="crop_x" size="30" type="text" /><br />
<input id="crop_y" name="crop_y" size="30" type="text" /><br />
<input id="crop_w" name="crop_w" size="30" type="text" /><br />
<input id="crop_h" name="crop_h" size="30" type="text" /><br />
<input id="crop" name="crop" type="submit" value="Crop!" />
</form>
Response error is:
ActionController::InvalidAuthenticityToken in CropsController#update
ActionController::InvalidAuthenticityToken
Rails.root: /home/mlzboy/my/crop2
Application Trace | Framework Trace | Full Trace
There is a view helper called form_authenticity_token that returns the current session's authenticity token.
In your view.html.erb:
<form action="/blah" method="POST">
<input name="authenticity_token" value="<%= form_authenticity_token %>" type="hidden">
<input name="first_name" type="text">
</form>
This answer is first for rails form token tag in Google so to keep it simpler for future googling generations: just use token_tag, it's a helper defined in ActionView::Helpers::UrlHelper that returns hidden input with form_authenticity_token as default value.
To generate the token you have to use the method: form_authenticity_token as it was correctly noted by #flitzwald. Since it is rediced in a active controller's concern, you must include the module into a controller expclicitly before using as follows:
include ActionController::RequestForgeryProtection
# use
def set_csrf_header
response.headers['X-CSRF-Token'] = form_authenticity_token
end

Resources