How to handle authentication failures on ajax calls? - ruby-on-rails

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

Related

Devise Rails API

can someone will explain to me this line of codes?
skip_before_filter :verify_authenticity_token, :if => Proc.new { |c| c.request.format == 'application/json' }
and should I use it? Why and why not. Thank you.
The verify_authenticity_token is a before_action (a method called before every controller action, known as a before_filter prior to Rails 4) that Rails uses to protect from CSRF attacks.
You can read more about how Rails does this here.
What this line of code is saying is: "if this is a JSON request then skip the CSRF check for this controller".
This is useful for JSON APIs which need to be made available to remote sites which are not on the same domain, and therefore would fail the CSRF check. This is safe, provided you make sure the API is being authenticated properly. However, if your controller is NOT going to be used by an external web application (and you are just doing AJAX stuff on your own site) then don't turn off the verify_authenticity_token check.

#! causing routing issues using Angular/Rails with multiple layouts

I'm developing an AngularJS application with a RoR backend and ran into an issue when using multiple layouts. The application uses one layout when rendering pages to an unauthenticated user, and changes to another layout once the user is authenticated.
The layout is server on the initial pageload, and is managed by Rails. Some sample code illustrating how we're loading the different layouts based on the route:
class SampleController < ApplicationController
layout :current_layout
def current_layout
"layout" unless request.xhr?
end
end
Sample controller for different section:
class SampleController2 < ApplicationController
layout :current_layout
def current_layout
"anotherLayout" unless request.xhr?
end
end
This is defined separately for the controllers managing the authenticated/unauthenticated users, and basically serves up the proper layout. We're using a XHR check to prevent routing loops when Angular comes into picture.
So this works fine in most browser, but breaks when using IE9. Angular falls back to using #! URLs in IE9, so Rails has no idea which controller to load since the hash doesn't get sent to the backend. In this case, Rails loads the root and it's associated layout. If the authenticated section is set as the default, then it loads this layout even for unauthenticated users, and vice-versa.
So basically, I need to find a way to make this multiple layout application work properly even in browsers which don't support HTML5 pushState. I've checked all over the place for a proper solution for this and couldn't come up with anything yet.
Alright, so after testing lots of tweaks on both the client and server side, I ended up using the following solution. Basically, I just stopped exposing Angular to multiple layouts and dealt with that in Rails. Of course, this does put a limitation on users.
The app is divided into two main sections: dashboard and authentication. So in the backend, we restrict the users from accessing any authentication pages if they're logged in, and obviously dashboard pages can only be accessed if the user is authenticated.
The root of the problem was that Rails had no way of knowing which layout to serve, if a layout should be served at all. We used the cookies to differentiate the authenticated and unauthenticated users. If the cookie is set, and it passes the authenticity testing, then the dashboard layout should be loaded. Otherwise, any user trying to access the dashboard will be redirected to the authentication section:
class DashboardController < ApplicationController
layout :current_layout
before_filter :authenticate_user
def authenticate_user
if request.xhr? && !cookies[:access_token]
redirect_to "/login"
end
end
def current_layout
if cookies[:access_token]
"dashboard" unless request.xhr?
else
"application" unless request.xhr?
end
end
end
Similarly for the authentication section:
class AuthenticationController < ApplicationController
layout :current_layout
before_filter :redirect_if_authenticated
def redirect_if_authenticated
if request.xhr? && cookies[:access_token]
redirect_to "/dashboard"
end
end
def current_layout
if cookies[:access_token]
"dashboard" unless request.xhr?
else
"application" unless request.xhr?
end
end
end
So main points to note here:
If the request is an XHR, don't serve the layout again. Serving the layout again will cause infinite loading loops in IE9, possibly in other IE versions as well.
Since we're not getting the URL Fragment at the server side, we have no way of knowing which layout should be loaded. So we're using the cookies as the source of truth, and access is controlled solely based on this.
This introduces some asymmetry on the clientside: In some cases, the url and layout will remain if the user types in the URL for another section. Since we can't do anything based on the URL from the server, and since cookies remain unchanged in this case, this has to be handled by Angular.
For the last point, the authentication section in my case only had a few possible urls, so I just redirected if I got a regex match as follows:
if(Modernizr.history) {
if(!/(login|sign|pass)/.test(location.pathname)) {
$location.path('/');
}
} else {
if(location.hash.length > 3 && !/(login|sign|pass)/.test(location.hash)) {
$window.location.href = '/';
}
}
Checking for the history API here, because in older IE browsers(and I guess in other older ones as well?), $location.path('/') wasn't reloading the entire page consistently. When using this, the $routeProvider configuration should also conditionally load the layouts, for cases where we redirect to root /:
$routeProvider
// Code for routes
.otherwise({
// Of course, add something to check access_token authenticity here as well :P
redirectTo: $.cookie('access_token') ? "/dashboard" : "/login"
});
So, with these couple tweaks, the app is working properly in all browsers and functioning with multiple layouts. Of course, I know nothing about RoR, so I'm hoping someone is going to have a few improvements to suggest, or a better answer. ;)

Rails: json response from secure action

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.

Is it possible to specify two root pages in Rails (one for anonymous user another for logged in user)

I am building a product that has static pages and dynamic pages(product related). Both category of pages have different release life cycle. The marketing team working with the designer, release the static pages and the product pages are released by the engineering team.
The static pages reside in public/home and they are self contained. They don't need access to the Rails infrastructure other than providing links.
In this setup, I am trying to implement the following behavior:
When an un-authenticated visitor launches http://www.xyz.com, the user should be taken to the static landing page.
When an authenticated visitor launches http://www.xyz.com, the user should be taken to the product landing page (LandingsController, index action).
In my current implementation, I check if the user is authenticated in the Rails world and render the static page OR the product page.
I want to know the following:
1) How do you handle such scenarios?
2) Is there a way to avoid entering the Rails stack for static home page.
3) Is there a customization for the root_path method to return different root based on the context
1) How do you handle such scenarios?
The common answer would look like this:
class LandingsController < ApplicationController
before_filter :login_required
def index
...
end
...
private
def login_required
if not_logged_in? # This methods depends on your authentication strategy
send_file "/your/static/path/#{params[:action]}", :type => "application/html charset=utf8;"
return false # Halt chain
end
end
send_file documentation
And, depending on the correspondence between each of your actions and your templates, you can further abstract the login_required method into the ApplicationController, and validate if the file exists.
2) Is there a way to avoid entering the Rails stack for static pages
Yes. You have to take my word for it, because I haven't done it myself, but you can use a Rack middleware to do that. Here is an example of how to do something similar, with the exception that instead of a redirect, you would serve the file statically (just set the headers and the results of File.read as content) This depends on the authentication library you're working with, though.
3) Is there a customization for the root_path method to return different
root based on the context
You cannot define a conditional route (that is, defining multiple routes in the routes.rb file), but you can override the root_url method in ApplicationController, assuming you are using a named path root in your route definitions. Something like
class ApplicationController
def root_url(*options)
if logged_in?
"/return/something/custom"
else
super(*options)
end
end
end
This, however, sound really a bad idea, since 1) You should point to the same url, and let the controller handle the request (your links should be blind of where to take you), and 2) It may potentially break other stuff that rely on the root_url and root_path methods.
Unfortunately, Rails' routing can only route requests to different controllers based on something in the request, making the per-request session data just out of reach. Your current implementation is certainly the most common strategy. I am guessing something like this:
def index
if logged_in?
# any logged in logic you need.
else
render :file => 'public/home', :layout => false
end
end
The only way to refactor this to make it feel less "icky" is to move that render call to a before_filter. Since the filter will have rendered?, your action won't get invoked at all. Of course, you could also choose to redirect_to another location for authenticated (or non-authenticated) requests in a before filter, which would solve the problem entirely.
The only thing you could do would be based on the non-existence of the session cookie.
Write a middleware component or Rack application (etc.) that explicitly handles the request if no session cookie is present. Similarly, you could use middleware to re-write the request, and then pass it onto the application layer.
Use a similar strategy as #1, but do it via web server configuration (Apache or nginx), avoiding the Rails app entirely.
But, it's definitely possible for someone to have a session and yet not be logged in (e.g. if they went to another page which you didn't handle this way), or even have invalid session data, so you wouldn't be able to actually eliminate the code you have now. These changes would only serve to increase the performance of the session-less requests, but unless those pages are causing a significant problem (which I doubt), so I would not recommend doing so.

Getting the current request in rails from a file in lib/

I've put all of my user-authentication code in one place, namely lib/auth.rb. It looks like this:
lib/auth.rb
module Admin
def do_i_have_permission_to?(permission)
# Code to check all of this goes here
end
end
I include this module as part of the application helper, so these functions are available in all the views:
application_helper.rb
require 'auth'
module ApplicationHelper
include Admin
# other stuff here
end
And I also include it as part of the application controller, so the controllers likewise can call the functions:
application.rb
require 'auth'
class ApplicationController < ActionController::Base
include Admin
end
So far, so good.
The problem is that my application is not like a normal web app. Specifically, more than one user can be logged into the system from the same computer at the same time (using the same browser). I do authentication for actions by looking at all the people who are logged in from that IP and if they can all do it, it passes.
What this means is that, if an admin wants to do something, that admin has to log everyone else out first, which is annoying. But we want the admin seal of approval on everything the admin does. So the suggestion given to me was to have it so the admin can supply a username/password combo on any page they would not normally have access to (e.g. an 'edit user' page would have these extra input fields) and the authentication routines would check for that. This means
Admin::do_i_have_permission_to?(permission)
needs to get at the current request parameters. I can't just use params[:foo] like I would in a controller, because params isn't defined; similarly request.parameters[:foo] will also not work. My searching has revealed:
The current search parameters are in the current request,
The current request is in the current controller,
The current controller is in the current dispatcher, and
I'm not sure the current dispatcher is kept anywhere.
That said, experience tells me that when I'm jumping through this many hoops, I'm very probably Doing It Wrong. So what is the right way to do it? Options I've considered are:
Just move all the functions currently in auth.rb into the ApplicationHelper where (I think) they'll have access to the request and such. Works, but clutters the hell out of the helper.
Move all the functions somewhere else they'll see those methods (I don't know where)
I'm just plain missing something.
In a typical Rails application, authentication information is stored in the active session, not the parameters. As such, it's pretty straightforward to write a helper that does what you want.
It seems rather unorthodox to create a module that is then included in ApplicationHelper. The traditional approach is to create a separate helper which in this case would probably be called AuthenticationHelper. This can then be included in any required controllers, or if you prefer, loaded into ApplicationController to make it available universally.
In general terms, Helpers should not include other Helpers. It is better to simply load multiple helpers into a given Controller.
Helper methods have full access to any instance variables declared within the controller context they are operating from. To be specific, these are instance variables only (#name) and not local variables (name). Helper methods are executed for a particular view as well.
Further, I'm not sure why a user would be providing credentials and performing an operation in the same step, at least for traditional web-based apps. Usually the process is to log in and then perform an action separately.
However, in the case of an API where each transaction is an independent operation, the most straightforward approach is to do is pull out the relevant request parameters that deal with authentication, establish some controller instance variables, and then proceed to perform the particular request given the constraints that the credentials impose.
The approach I usually follow for this sort of thing is to layer in an authentication structure in the ApplicationController itself which can perform the required checks. These are protected methods.
While it's tempting to roll in a whole heap of them such as can_edit_user? and can_create_group? these very quickly get out of hand. It is a simpler design to put in a hook for a general-purpose can_perform? or has_authority_to? method that is passed an operation and any required parameters.
For example, a very rough implementation:
class ApplicationController < ActionController::Base
protected
def has_authority_to?(operation, conditions = { })
AuthenticationCheck.send(operation, conditions)
rescue
false
end
end
module AuthenticationCheck
def self.edit_user?(conditions)
session_user == conditions[:user]
end
end
class UserController
# ...
def edit
#user = User.find(params[:id])
unless (has_authority_to?(:edit_user, :user => #user))
render(:partial => 'common/access_denied', :status => :forbidden)
end
rescue ActiveRecord::RecordNotFound
render(:partial => 'users/not_found')
end
end
Obviously you'd want to roll a lot of the authority checks into before_filter blocks to avoid repetition and to promote consistency.
A full framework example might be of more help, such as the Wristband user authentication system:
http://github.com/theworkinggroup/wristband/tree/master

Resources