Rails: json response from secure action - ruby-on-rails

Originally I had quite usual ajax form with json response:
def create
# created logic omitted as most likely irrelevant
render :json => {:success => true} #over simplified JSON for debug purposes
end
So far so good, works as expected. I've added security on the create action via ssl_requirement gem:
class RegistrationsController < Devise::RegistrationsController
ssl_required :create
# rest of the code omitted, 'create' action as above
end
All of a sudden I get the following in my form response (observing in HttpFox):
Error loading content (NS_ERROR_DOCUMENT_NOT_CACHED)
The create action runs as expected (enforces HTTPS, creates an object but... fails in the browser. To be specific, fails in Firefox (works on chrome). Any clues and ideas will be greatly appreciated.
Regards,

I'm not certain, but I believe your problem has to do with cross-site AJAX requests.
The fact that you are using a different protocol is making firefox believe you are making a cross-site request. Chrome, I believe, is less strict with this restriction when on local. Try visiting the site itself over https and see if the AJAX request goes through.

Related

How to handle authentication failures on ajax calls?

My rails controller has two types of handlers, one type is conventional response with a web page, another is designed to respond to $http get requests from Angular, and returns json to be processed by the pages javascript code.
I use devise, and I this code at the top of my application controller
protect_from_forgery
before_action :authenticate_user!
after_action :set_csrf_cookie
The problem is that when for example, the login goes stale, I think authenticate_user is returning my "unauthorized" web page to the caller, rather than the json that would inform the caller that the current user is no longer authorized, and then I could handle the condition on the client side properly.
Any thoughts on efficient way to do this, withoug having to take out authenticate_user! from the application controller.
Most of my controllers have handlers for about 15 routes, about 50/50 which of them are designed to return json to ajax calls, and the others return web pages. I like the security that authenticate_user! in the application controller provides, and am hesitant to remove it and instead have to have different code to handle security in each of my methods.
Thanks.
To understand how this works you really got to get into Warden (which devise is built on top of) and Rack. What before_action :authenticate_user! does is call Warden.authenticate! and asks it to identify a user. Warden identifies users by using strategies. A strategy can be just using session[:user_id] to find a user from the database (which what happens 99% of the time in Devise) or something more novel like HTTP Basic Auth.
If all the available strategies fail then the failure app is called. This is a Rack application. In Devise this is just a basic Rails controller (Rails controllers are Rack compliant applications) that usually returns a redirect. If you are running Rails in the development environment you may get a HTML response though as the error handler that shows you those friendly little exception pages kicks in.
You can customize the response by providing your own failure application:
class CustomAuthFailure < Devise::FailureApp
def respond
self.status = 401
self.content_type = 'json'
self.response_body = {"errors" => ["Invalid login credentials"]}.to_json
end
end
# config/initializers/devise.rb
config.warden do |manager|
manager.failure_app = CustomAuthFailure
end

Using responders gem with Rails 5

I'm using responders gem to dry up my controllers. Here's my current code:
class OfficehoursController < ApplicationController
def new
#officehour = Officehour.new
end
def create
#officehour = Officehour.create(officehour_params)
respond_with(#officehour, location: officehours_path)
end
def officehour_params
params.require(:officehour).permit(:end, :start, :status)
end
end
The problem that I'm facing right now is:
When I send valid parameters to create, it redirects to officehours/ as expected, however when I get 422 (validation error), it changes the URL from officehours/new to officehours/ (however it stays at the form page... idk why). The same happens for edit/update actions.
So, I want to stay at the .../new or .../edit when I get 422 error, how can I do this?
I don't think the issue comes from the gem. It just follows RESTFUL API, so does Rails.
That means the path for creating office hours is /officehours. Let's talk about your case:
There is nothing to say when we creating successfully. In your case, you redirect users to officehours_path.
When we creating unsuccessfully, we need to re-render the error form to users. But we were rendering the form in create action. As I said above, the URL for creating is /officehours, so you will see the /officehours instead of officehours/new
In order to keep the url /officehours/new:
We can set /officehours/new instead of /officehours for :create action. But you should not do that, are we going to break RESTFUL API?
Creating via AJAX to keep the URL persisted and you have to handle everything, eg: set flash messages on client, using Javascript to redirect in case of success, render the form error in case failure, ... And I don't think the gem help you dry up your application anymore.
Hope it helps!
I don't think so that it's a problem in responders gem, as I've noticed the same in rails applications. It seems like the default behaviour of rails applications.
take a look at this link for the explanation.

Using response_with in Rails, how can I prevent execution of create action on not accepted MIME types?

I'm building a RESTful API using Rails 3.2.21. The API should only response to xml or json for now. I have a simple resource called Users with a create action, that creates a new user on a POST Request.
Here is the Code for doing that:
respond_to :json, :xml
def create
#user = User.new(params[:user])
#user.save
respond_with(#user)
end
Everything goes fine so far, but then I tried to check the error cases. So if I do a POST request to /users.html, the answer is '406 Not Acceptable'. What is correct. But then I saw in the database, that the user was created anyway. So the create action is executed although the requested accept format (html) is not supported and a 406 error is responded.
I don't know if this is intended. Until now I really liked response_with from Rails, because it does lots of stuff for me, but this behaviour seems to be odd. From the perspective of a client you try to create a new user, receive 406 and you obviously assume that the request failed, so the user is not created, right?
Since I've defined the accepted MIME types in the class method respond_to, it should be possible for Rails, to prevent execution of the entire action. Sure, respond_to is only related to the http response, but then for the client it is still unknown which part of the POST request succeeded and which failed.
Are there any setting or additional functions in Rails what I've overseen or is this just 'not thought to the end' in case of POST requests using respond_with or is this behaviour even intended to react like that? Of course I can add some custom before_filters for checking the MIME types, but then I can also remove respond_with and handle everything on my own.
I'm looking for a nice and clean solution for that problem using respond_with.

How to profile a rails controller that returns a json response with rack-mini-profiler?

I am using rack-mini-profiler in my rails 3.2 project.
In gemfile:
gem 'rack-mini-profiler'
Everything works great. But my application is mostly a set of json endpoints. So while it is very useful to be to able to inspect the performance of html pages, I would like to also be able to see the performance of controllers that return json.
Example of my controller:
class UsersController < BaseController
def json_method
# you don't see the mini profiler ui for this controller
render json: { users: [:foo, :bar]}
end
end
If I go to localhost:3000/users/json_method, I see my json response but not the profiler ui.
In development, by default, the rack-mini-profiler gem collects the previous JSON call and presents it in the menu accessible from the HTML page. No code change required.
So, make your JSON request, then hit any other HTML page and it will be available in the list. We use this to great effect. If your Rails app is a JSON API service only, make sure you have a 404.html in your public folder at least, then hit something like:
http://localhost/404.html
In the second tab, you can visit this URL /rack-mini-profiler/requests Where you can see the last request log
if rack mini profiler can’t display the results, it will collect them until it can on the next HTML page. So, the solution that I am using is to:
make the JSON request and
then hit an HTML page of my choice.
The results will appear, along with the most recent HTML profile.
http://tech.eshaiju.in/blog/2016/02/25/profile-api-endpoints-using-rack-mini-profiler/
As a first solution, you can just set the format to html, and render inside the html page:
The controller:
class UsersController < BaseController
def json_method
#users_json { users: [:foo, :bar]}
render 'index', formats: ['html']
end
end
And in app/views/users/index.html.erb:
Users:<br/>
<%= #json.inspect %>
I don't care so much about my json result, now I have the profiling ui.
A solution where I have the profiling ui without changing my controller would be much better.
Note that in a new Rails API project initialized using rails new api_name --api, the ApplicationController inherits from ActionController::API, instead of ActionAcontroller::Base. In this case, mini-profiler might not load when your HTML page is shown.
I had to change the base class to ActionController::Base to make it work. If in your app you see no requests to load resources from mini-profiler on your HTML page, you may want to try this change. Took me a long while to figure out.
Also note that you do need to have at least the <body> tag in your template to be rendered, otherwise the mini-profiler divs will not be properly injected.

Redirecting to an external URL with Rails doesn't work

In a controller I attempt to do
redirect_to #url
#url is an https url which is correctly formatted
When I do it nothing happens in the browser
Logs show that there is a redirection, but in chrome inspect I see
Request URL:https://..myurl...
Request Headers CAUTION: Provisional headers are shown.
Origin:http://localhost:3000
Referer:http://localhost:3000/cars/105
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.76 Safari/537.36
Query String Parametersview sourceview URL encoded
tId:6f6bfeaf-fd47-42ae-8e43-6b7118d21b0b
The network tab shows that it was canceled..
Am I missing something? Why isn't the redirection happening?
Based on your statement that you saw the request was canceled in the network tab in Chrome, it sounds like you're trying to do this in an AJAX request. Cross-domain AJAX requests are not supported by default. If you have control over the https://..myurl... application, you can implement CORS or JSONP.
Edit
A third approach, which is what you mentioned doing in one of your comments, is to get around the restriction on cross-site redirects for AJAX requests by redirecting on the client side, after the AJAX request completes (by setting window.location). However, if your ultimate goal is to have the redirect happen transparently—as part of the AJAX request rather than after it—you will need to implement CORS or JSONP.
After searching a while on (CAUTION: Provisional headers are shown)
I think it is related to some chrome extensions such as ad blocker or net-internals
Quoting another stackoverflow question"The message is there because the request to retrieve that resource was never made, so the headers being shown are not the real thing. As explained in the issue you referenced, the real headers are updated when the server responds, but there is no response if the request was blocked."
You can make sure that all the extension that are capable of blocking requests are disabled and try it again.
I think you need to specify that the protocol you want to use is using SSL. The ActionController::Base#redirect_to method takes an options hash, one of the parameters of which is :protocol which allows you to define:
redirect_to :protocol => 'https://'
If all of your redirects are going to be done via SSL it would make sense to put this in a before_action in your controller. You could define a method like:
def redirect_via_https
redirect_to :protocol => "https://" unless (request.ssl? || request.local?)
end
And add this to your controller:
class SpecificController
before_action :redirect_via_https
end
Can you visit the page and see it in Chrome without any security warnings?
I've had css etc. not load because I was requesting it via https to a local server with a unverified certificate. It's possible Chrome is checking behind the scenes before going to the url but I don't know that for sure.
Try redirect_to https://www.google.com and see if that works.
what is your rails version, because redirect_to method is working for external urls.
class HomeController < ApplicationController
def index
#url = "https://facebook.com"
redirect_to #url
end
It works.
I would suggest several troubleshooting steps:
Try another browser (Safari, Firefox), if it works on that browser, then perhaps a Chrome extension is blocking your visiting that redirected URL.
Try redirecting to other URLs, try both with and without https.
Check if your Rails action is an AJAX call or not. If it's an ajax request then redirect_to wouldn't work.
I think that the suspicions around AJAX are well founded. It's really hard to guess at what might be going on, given the narrow view into your application that we are presented with. Try changing
redirect_to #url
to
if request.xhr?
render :update do |page|
page.redirect_to #url
end
else
redirect_to #url
end
Also, as an aside, #url probably has excessive scope, given the redirect. You can most likely use url just as effectively.
Edit:
In light of information I previously overlooked, I am certain that the previous solution will correct the issue. Basically, some browsers are stricter about how they handle responses to xhr requests. With a standard redirect_to, you can potentially send a "regular" response for a xhr request. This can happen even if you have a *.js.rjs or *.js.erb that would typically contain the redirect code, as the redirect_to call will prevent that code from even being executed. If you have an app that has js that gracefully degrades, there is a likelihood that you will end up running into this issue sooner or later, as you appear to have a couple of times. I've found a couple of good ways to handle the basic issue.
Option 1:
One way is to simply adjust the redirect to look like this: redirect_to( #url ) unless request.xhr?, and then add the appropriate redirection logic to your *.js.erb file. This method is nice if you have additional javascript that needs to be run, in addition to the redirect.
Option 2:
Another solution that works well is to add something like this to your application controller:
def redirect_to(args)
if request.xhr?
render js: "window.location = '#{ _compute_redirect_to_location args }'"
else
super args
end
end
This is nice, because you don't need to sift through and modify your existing code, it just starts working everywhere. The other nice thing is that you can still qualify the redirect as the previous option, to allow for more complex javascript. You can even go crazy and add some additional application specific options, like a flash message, if you want to further extend the method and really make it work for you.
Option 3:
If you're not down with overriding existing methods, you could add something like:
def firm_redirect_to(url)
if request.xhr?
render js: "window.location = '#{ url }'"
else
redirect_to url
end
end
But then you have to modify all of your code.
I personally prefer a variation of option 2... Anyway, hope this helps - good luck.

Resources