Allow AJAX Through Devise (Rails) - ruby-on-rails

I've just added and configured the devise gem. It's working great except for blocking my form's autosave AJAX calls.
At the top of my controller, I have:
before_filter :authenticate_user!
My AJAX call comes to the same controller:
def autosave
#TODO: update relative entry
#TODO: verify user logged in
#TODO: verify entry belongs to relative user
render content_type: 'text/xml', inline: "<result status='ok' />"
end
Of course I could instead declare my before filter like so:
before_filter :authenticate_user!, except: :autosave
..but this offers nothing to prevent anyone from calling this controller function at any time.
What would be the best way to allow this function to be called? I still want to ensure that only logged in users can make the call and that the given record they're editing belongs to them.

Not entirely sure what you're trying to do, but it sounds like you just need to add a bit of logic to your autosave. With this setup the function will still be called per se, but whether or not it does anything is a another mater entirely unless the conditions are right.
def autosave
#Verify that the user is signed in and he has ownership of entry
if !current_user.nil? && entry.user == current_user
render content_type: 'text/xml', inline: "<result status='ok' />"
else
#do nothing nothing or redirect with error here
end
end
Since you're making an AJAX post you have to provide some additional information to your ajax call as a security countermeasure, namely a CSRF security token. See Devise not setting current_user on Ajax post even though correct x-csrf-token is included in request header for an example AJAX request using a security token. Also, make sure you also include <%= csrf_meta_tag %> in your head tag.

Related

When and how request sometimes found to be unverified. ruby on rails 3.2

# This is the method that defines the application behavior when a request is found to be unverified.
# By default, Rails resets the session when it finds an unverified request.
def handle_unverified_request
reset_session
end
I have seen this explanation at Rails 4 Authenticity Token
now my question is when and how every request sometimes become unverified? how it was hapenning? and when.
thankyou, i have tried to search it but i have seen explanation so deep-technical hence i can understand in an easy way
Rails adds a CSRF authenticity token to form submissions.
If you have a Rails-generated form in your browser and you inspect it, you'll see something like this:
<input type="hidden" name="authenticity_token" value="/LV6706J3W++oCASgg8+wuySgIksE9BNjamMbMW8Zv+G039yyxbpcRpUlUzuVbVvodKtDnUbknwo+jsBzsoO8g==">
Rails checks this hidden tag on form submission to make sure it's the same form that Rails generated in the first place. This helps prevent CSRF attacks
If this field's value doesn't match what Rails expects, it goes to the handle_unverified_request method you mentioned.
And it's not just forms, Rails can add tokens to the session to make sure it can match a request to an active session.
Regardless of the source, if Rails gets a mis-match, it wants to handle that as a security threat.
In essence, Rails is asking you "what should I do when I think the request I received is unverified and potentially an attack?"
In this case, Rails would reset_session which logs out the current_user.
Rails allows you to turn off or limit CSRF protection in cases where you may need to do strange things, but it's not advisable in any instances I'm familiar with.
You can do this by changing the options on protect_from_forgery as mentioned in the SO post you linked.
def handle_unverified_request
reset_connection
# validate only for html submit and not for ajax
if request.post? && !request.xhr? && request.content_type != 'multipart/form-data'
redirect_to controller: 'logout', action: 'index', is_invalid_token: true
end
return
end
and then i have log out controller
if !params[:is_invalid_token].nil?
flash[:notice] = "You dont have access with this."
flash[:notice_header] = 'Forbidden Access'
end
redirect_to :controller => 'login', :action => 'index'

Accessing Doorkeeper Application Information before login redirect

Explanation
I am wanting to halt the authorization process of a client app (running OAuth2) coming to the parent app (running Doorkeeper) in order to see which client app is requesting a login. That way I can then look up the clientID and dynamically build a custom login screen for the client app. Right now, my client goes to parent, AuthorizationController is called, but before new is called and I can get the params[:client_id], authenticate_resource_owner! is called with a before_action. That then sends the user to the login page if they are not already logged in with the parent. So, before I can get the param, it is being redirected.
Question
The authenticate_resource_owner! is held in a Doorkeeper helper file. I thought that I set it up correctly to bypass the default helper and go to mine where I can try and grab the param and save in sessions before the redirect, but I guess my route is not set up correctly and I can't find any documentation on how to correctly call it. Can anyone help?
Code
Code for setting up the client:
def setup_client
#client = Application.find_by(uid: params[:client_id])
session[:client_name] = #client.name
authenticate_resource_owner!
end
I know that the first 2 lines work as I placed them in the CustomAuthorizationsController with a byebug and it triggered after the login and before redirect back to client and showed the client name stored in a session variable.
In my config/routes.rb
use_doorkeeper do
controllers :applications => 'doorkeeper/custom_applications'
controllers :authorizations => 'doorkeeper/custom_authorizations'
helpers :doorkeeper => 'doorkeeper/doorkeeper'
end
Helper file is located in app/helpers/doorkeeper/doorkeeper_helper.rb
Error
When I start up my server I get:
: from ~/ruby-2.5.0/gems/doorkeeper-5.0.2/lib/doorkeeper/rails/routes/mapper.rb:12:in `instance_eval'
~/settingsParentApp/config/routes.rb:65:in `block (2 levels) in <top (required)>': undefined method `helpers' for #<Doorkeeper::Rails::Routes::Mapper:0x00007ffd539b9c10> (NoMethodError)
Conclusion
Am I even doing this right? Is there a simpler way built into Doorkeeper that I am not seeing to get this information to customize the login screen? Or is there some error that I am not seeing in how I am calling the helper file?
After thinking through my problem in order to ask this question, a solution dawned on me. I tested it out and it worked. I forgot that in a controller, the before_action statements are called in the order they are presented. So, my solution was just to reorder my statements to call the setup_client first before the authenticate_resource_owner!. This set up the session variable before redirecting to the login screen and then allowed me to have the variable available for use.
Code
Within my config/routes.rb file:
use_doorkeeper do
controllers :applications => 'doorkeeper/custom_applications'
controllers :authorizations => 'doorkeeper/custom_authorizations'
end
This custom route bypasses the doorkeeper default authorization controller and goes to a custom one which inherits from the default controller. So, all I need within this custom one is this code:
Found: app/controllers/doorkeeper/custom_authorizations_controller.rb
module Doorkeeper
class CustomAuthorizationsController < Doorkeeper::AuthorizationsController
before_action :setup_client
before_action :authenticate_resource_owner!
def setup_client
#client = Application.find_by(uid: params[:client_id])
session[:client_name] = #client.name
end
end
end
This code is then run before it looks to the Doorkeeper's default AuthorizationsController and thus calls setup_client first. The session variable is then saved and in the login screen I can call it this way:
<%
if session[:client_name].nil?
#client_name = ''
else
#client_name = ' for ' + session[:client_name]
end
#page_name = "Login" + #client_name
%>
And then in header of the page I call this within the HTML:
<h1><%= #page_name %></h1>
I may do more fancy things later, like saving client icons/logos and color schemes to make branding specific on the login page, but for now, this basic issue has been resolved. Thank you all for acting as my sounding board and problem-solving ducks... if you know of that reference. :-) Happy Coding!

Using Devise, how do I customize a failed authentication?

I have a controller that uses Devise to authenticate actions. I'd like the default behavior of responding with a 401 on every action except for one. I'd like to proceed with the request but provide a different response body.
What method do I provide or override to accomplish this?
First you will need to skip the standard authentication on the page you want to do this on..
before_filter :authenticate_user!, except: [:mydifferentcontroller]
then you will need add some logic for the controller to bring you to your alternate response
def mydifferentcontroller
unless user_signed_in?
## add redirect_to if you want to send them to an entirely different page or whatever change in logic can go in here
end
end
Or if you just want to change a certain part of the page you can use the user_signed_in? in the view as well
- if user_signed_in?
.classyclass You're signed in
- else
.classyclass You're not signed in
if that's what you meant....

How to validate my controller action was called from within my Rails app

I have a simple user registration form.
When the user fills out the form and clicks "Submit" I have a JavaScript event that intercepts the submit and uses AJAX to call the validate method below to check the form data before submitting. If the form data is ok it continues with the submit, but if it's not it cancels the Submit and displays a warning on the page. It's a simple way of doing real-time client side validations.
# app/controllers/users_controller.rb
# Validates the form data. Returns an error message response in the format
# { response: "Invalid email format!" }
#
# A blank response means success
#
def validate
if request.xhr? || <request came from another method in this controller>
# Run various validations on input data and determine a response
# response_text = ...
render json: { response: response_text }
else
redirect_to root_path
end
end
def create
if JSON.parse(validate)["response"].blank?
User.create(...)
# Other stuff
end
end
However when the submit does eventually pass and go through, it sends a POST to the create action to create a new User. That method calls validate again (the cost of re-validating is minimal) to ensure that no one bypassed the form and submitted a malicious request directly to the server.
So my validate method has to respond to both (a) AJAX calls and (b) "internal" calls from within the app. All other calls to the validate action should just redirect to the root path.
I can tell whether the call was an a AJAX call pretty simple using request.xhr?.
How do I check whether the action was called internally and not by a user?
Taking a step back, is this a good general approach for validation? Any thoughts to improve on it?
Thanks!
Rails generates an authenticity token whenever a user views a form and stores it as a random string in the HTML. That token is also stored in the session and is therefore invisible to the user. Upon receiving a request, your application will compare the tokens to verify whether the request was generated from your form.
TL;DR: Don't worry, you're already protected.
I don't have a specific answer, but do have some information which could help. I'll gladly delete this if required...
AJAX calls and "internal" calls
You have to understand that XHR requests can only come from your app -- CORS (cross origin resource sharing).
Ajax calls can only come from your own domain, so don't think a malicious hacker could run some scraper or whatever -- you choose which domains are permitted to send XHR requests.
So when you're calling...
if request.xhr?
... as part of a validation, you need to scope when XHR will be used.
On the same note, what validation are you performing?
If you're validating input data, Rails handles this at the model layer. We've done something similar (click login/register at top):
The base structure of Rails is MVC, which means that your controller is only responsible for taking a request & building the appropriate data objects out of it.
Thus, if you're "validating" an input, what is there to validate apart from the data itself (handled by the model)?
As #MarsAtomic specified, too, Rails uses a CSRF token to provide some level of authentication for a form submission.
--
You could easily use the following:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
respond_to :js, :json, :html
def create
#user = User.new user_params
respond_with #user.save
end
end
If you then sent the request from your front-end as json, you'd get back the created user OR the errors:
#app/views/users/new.html.erb
<%= form_for #user, remote: true, format: :json do |f| %>
<%= f.text_field .... %>
<%= f.submit %>
<% end %>
#app/assets/javascripts/application.js
$(document).on("ajax:error", "#new_user", function(xhr, status, error) {
// do stuff with response
});
In your create method, you want to return a #user if it's not valid (if they skip the ajax validate).
If that's the case, you don't want to do User.create, instead, create an instance and try to save it. If you do all this, you're doing the normal controller action for create and there is no need to call validate internally.
def create
#user = User.new(user_params)
if #user.save
# Other stuff
end
end
And, it's not a good practice to create a json response and parse it to see if an object is valid. Instead, either test with valid? or just try to save and get back a true or false.
In other words - keep your validate method separate from your create method. They'll both rely on valid? eventually.

How to setup routes when the controller only has edit and update?

I can't seem to figure out how to get my routes setup properly.
In my app, I have a view that lets site owners update their address information. The new and create actions are part of the signup process and are located in the signups_controller. The edit and update actions are in the settings_controller.
When the user goes into the settings area, he/she sees only the edit form. When filled out, the user is then returned to the same form with a flash message, or error message. Here is what the controller looks like:
class SettingsController < ApplicationController
def edit
#account = current_account
#account.companies.first
#account.companies.first.addresses.first
#account.companies.first.phones.first
end
def update
#account = current_account
if #account.update_attributes(params[:account])
redirect_to edit_setting_path
flash[:notice] = "Success!"
else
render :edit
end
end
end
In my routes, I simply have:
resources :settings
The link to this area of the site is a basic RESTful named linke, with the parameter options:
edit_setting_path(:id => current_account.id)
When the user arrives to this page, they see the following URL:
http://domainname.com/settings/1/edit
When they submit the form and get errors, the URL changes to:
http://domainname.com/settings/1
Why is the URL changing -- I'd rather it not? Is there a way to make it stay the same as the initial edit view? I've tried doing a redirect on a failed update, but then I don't get the error messages.
Any ideas?
To answer your "why" question: The URL is changing because it's reflecting the URL of the failed request - which in this case is a PUT request to that URL (/settings/1). You've submitted the form and the submission of that form (correctly) points to that URL. This is a result of the RESTful routes that the helper gives you. Since the logic in your action, falls through to the render :action, there is no redirect and the form simply re-renders on the page using the same data available in this action (which is why you can see the errors).
If you want to redirect back to the edit page, yes, you will lose the errors that have been set in the #account instance variable since the redirect will reset (re-query for) the account.
You could add a route that matches a PUT to /settings/1/edit and point it to your update action and change your form etc. In short, I wouldn't recommend this, but it should work.
completely untested but attemptable:
routes.rb
put "/settings/:id/edit", :to=>"settings#update", :as=>"update_setting"
resources :settings, :except=>:update
your form would also have to submit to the update_setting_path (which also means it's not reusable for a new object... ew)
First you should read up on The Rails Guides for Routing. They will help a lot to understand why its working like that.
Secondly, to accomplish what you are trying to do, you will need to add manual routes via the match call. You'll need something like this.
match '/settings/:id/edit' => "settings#edit"

Resources