Adde authenticity token manually but it is not verified in backend - ruby-on-rails

I had the <%= csrf_meta_tags %> in my HEAD tag, but I created the form manually, so the authenticity_token hidden field was not getting inserted in my form.
So I added the authenticity_token manually:
<input name="authenticity_token" value="<%= form_authenticity_token %>" type="hidden">
When I send my website's default contact form (user is not logged in) the action that handles it should verify the token and allow/deny the request. But it is not: I use jQuery to empty the field and submit the form but not error or exception are thrown.
I got this line protect_from_forgery with: :exception in application_controller.rb and put the same in the foo_controller.rb that extends application_controller and has the action that responds to the contact form.
So what am I missing? What do I have to do to have this form verified in the backend?
Thanks a lot!

When the submitted auth token is missing or not matching the one in session, Rails does what protect_from_forgery with: specifies but the request handling is not terminated. What happens is session gets destroyed so request is handled as belonging to a non-logged in user.
Here is an excellent writeup discussing how protect_from_forgery works.
It's hard to tell without looking at the code but I'd say your form submission code either does not pickup the auth token input value or it becomes stale before the request is made. The latter can happen for in a number of scenarios such as page loaded from the browser cache or browser tabs used to login/logout in parallel with the page in question.

Related

`InvalidAuthenticityToken` error in rails post route

I have a rails app with a POST url which creates some resources.I have a page with a form which takes in all the information and does an AJAX call to the POST url without authenticity token.
Am doing data["authenticity_token"] = "";, before doing the AJAX call.
Parameters logged on serverside are like below
{"utf8"=>"✓", "authenticity_token"=>"", "company_customer"=>{"name"=>"Anand"}}
The resources are created without any error(I have protect_from_forgery with: :exception in my ApplicationController).
But when I tried to call the same POST url from Postman, I get InvalidAuthenticityToken error.
Why am I getting the error?
How does the rails app verify the authenticity of the POST request in first case?
In the first step i.e. from browser , you might be having some session id in the cookies but here not.
Also, if you were hitting by remote:true option, it will take the authenticity token from the page in the hidden field.
For more details , check your logs in both cases.
This token is automatically added in as hidden field when you are using form_for helper method to generate forms, so Rails makes sure the request comes from one of your forms.
You should unprotect your controller action when requested from postman or any other app, see how to here : https://stackoverflow.com/a/22715175/8352929
You can find how CSRF works from here. I recommend you go through it.
Whenever you use form_for Rails adds one hidden input field to your form which looks like the following:
<input type="hidden" name="authenticity_token" value="doLYVxrkhdrzn7zzriHXjFE6ZhNCuXVxLrau4ouENmuKKC/SWp2NMM/MeL/Ji2tDvzNcJHVN/Hc0LIluL3o5QQ==" />
Also, Rails include CSRF token in the meta tags of the website which looks like the following:
<meta name="csrf-token" content="zxnmBxg81JUQPG/C/wb3HRCah0m9Xe2A+gZ5N0Oy7cfwC+dF4hC325WxdVDLfkIxcw/CR/xyaC1phpvZ4EcgQw==" />
So, when you use Rails form_for or something similar to make AJAX call(may be with remote: true) the authenticity token is sent to the server. Which was not present when you tried to send the same request with Postman.
If you copy the CSRF token and add it to Postman params, the request will be completed successfully.

Rails not keeping session data when doing ajax with remote=true

I am working with a rails app and trying to do an ajax request with rails remote: true . The request is supposed to trigger one of my controller actions.
<%= form_tag my_action_path(#version), {remote: true} do %>
In the application controller there is a before_filter callback which executes before the action is performed. It checks for session user id like so:
if session[:user_id]
This check fails forcing redirect. When I check session[:user_id] it's nil, which I guess means that the session cookie is not passed. I have a bunch of js that do ajax to the controllers and interestingly they all work, passing the check. So two questions:
Do I need some extra code to make remote:true work? Is there a way of setting a session cookie or smth?
Is this a correct way of verifying unauthorised ajax requests or is there a better way.
I'm using Rails 4
Thanks
I'm using devise, so I'm not sure if this is directly applicable, however, you also have to pass the authenticity_token in the form tag as well.
<%= form_tag my_action_path(#version), {remote: true}, authenticity_token: true do %>
It might be something separate, but similar. I think this will just work for you though.
Another thing to look at is look at a form that works and look at what data is posted to your server in the console, because rails echoes the data passed to the server and you can use this to help us help you debug. It should have session_id or authenticity_token or something similar in all session requests that work (anything you do while you're logged in).

Rails authenticity token (CSRF) provided but being refused

I'm sending an AJAX request from my rails site to itself (to go from javascript to a controller). Rails refuses to allow the POST unless I supply an authenticity token, so I added one using
<%= csrf_meta_tags %>
and
var AUTH_TOKEN = "<%=j form_authenticity_token %>"
and everything was fine. However, a new customer recently installed the plugin that accesses my site and triggers the AJAX in the first place. But for this one customer--the authenticity token was denied, despite being supplied (I checked in the logs.)
I realize I'm not giving a lot of clues to go off of, but what could cause the authenticity token to be accepted in one situation and denied in another? More broadly, how is the authenticity_token generated anyways--a new one every single time the page is loaded?
Rails assigns a cryptographically random CSRF token to the the user session.
The server compares the value submitted for the authenticity_token parameter to the value associated with the user’s session.
One thing you especially need to be careful with is that if you are using fragment caching (which speeds up rendering by caching chunks of the view) you need to ensure that your <%= csrf_meta_tags %> are not cached since a stale csrf meta tag will lead to mismatch with the token stored in the session.
When posting with ajax, you need to forward the CSRF token with the X-CSRF-Token header.
var promise = $.ajax({
url: '/example',
type: 'POST',
beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRF-Token',
$('meta[name="csrf-token"]').attr('content'))
},
data: 'someData=' + someData
});

Landing Page for Age check in Rails App

I have built a marketing site for an alcohol brand and I need to check the user's age by adding a landing page before they can enter the main site. What is the best way to tackle the form, submit and validation functionality inside my existing rails app?
Should I just create a raw html form and use javascript?
Add a before_action to ApplicationController that checks if the verification has already taken place (i.e. if it is stored in a cookie, then check for the cookie, etc):
class ApplicationController
before_action :check_age
def check_age
# check if the user has already confirmed their age.
end
...
end
If it doesn't find this, then redirect the user to a controller action that renders a page with the age check form (i.e. AgeVerificationController#new)
Upon submit, set the cookie (or whatever you are doing to store this data), and redirect the user back to the page they were intending to visit (or kick them off the site if they say they are under age!)
You will need to include a skip_before_action on the controller you are using to handle the rendering and submission of the form, i.e.
class AgeVerificationController < ApplicationController
skip_before_action :check_age
...
end`
Using before_action is sometimes a bit of an anti-pattern if you start using it to do a lot of complex stuff, but in this case it is a fairly simple way of doing what you are looking to do.
even if you use javascript,you will need to store the age of the guest to help him out next time for a better user experience.So i will suggest you to save it along with the ip-address to recognise the guest,if you are not storing a unique parameter for login(such as email,mobile number etc).
Once you have the table ready to store this details...you have many options such as:-
first option is to that,before submit get the age of guest using jquery validation and pass it to the controller using form and store it.Use ajax for form submission so that
you can validate other elements as well
second option is to let the user visit the page and show a modal window popup in the middle,after five seconds when page has loaded by using settimeout to call ajax which in turn will call a controller method to render js file which will call a modal such as $(".myModal").show(); or render your own view to get user details such as:
$('#myModal').find('.modal-body').html("<%= escape_javascript(render(:partial => 'users/get_details')) %>");
$('#myModal').modal();

Get any user by SessionID (cookie) from Devise authentication system

A JavaScript piece of code sends UserId when initializing WebSockets connection. This requires to pass UserId from Rails view to JavaScript source.
I do not like this solution for two reasons:
I want JS code to be entirely in *.js file without having to include it as partial into layouts
UserId is easy to counterfeit and listen messages addressed to other user
The better solution is obvious: in any case each WebSocket connection from an authenticated user is accompanied by Devise's SessionID in cookie. I already found how to extract this cookie from handshake data and now there is one problem:
The WebSockets listener is running constantly in a background thread whether some user is authenticated or not. So, it does not have an access to user's session.
The question:
How to get an user or UserId by SessionID if I am not authenticated. Does Devise have some SessionID storage in memory which I could access (probably with some nasty hack).
Devise stores stuff by warden within the user session. So if you have no access to the session, there is also none to the user_id within this separate thread.
Since your are exposing the user_id anyway in the script, you can just set it into an additional cookie.
You could also implement an api enpoint returning the id.
You can render it into the page within some invisible 'data-' attribute
<body data-user-id='asfasdfeefifpf'> ... </body>.
Or at least you could just keep the code in the js partial, which sets the id and extract the lib code into separate file.
<%= javascript_tag do %>
window.user_id = '<%= j current_user.id %>';
<% end %>
http://railscasts.com/episodes/324-passing-data-to-javascript?view=asciicast

Resources