Connecting to rails with curl gives wrong response - ruby-on-rails

I've implemented Matteo Melanis great blog article A Simple Token Authentication Service for Mobile Devices. It works beautifully with the Chrome extension Postman. However, when I try to fetch a user authentication token with cUrl, I'm running into a bizzare problem.
First the development.log entry for the (successful) authentication fetch using Postman:
Started POST "/api/v1/tokens.json?email=my#mail.com&password=[FILTERED]" for 127.0.0.1 at 2013-11-25 11:28:21 +0100
Processing by Api::V1::TokensController#create as JSON
Parameters: {"email"=>"my#mail.com", "password"=>"[FILTERED]"}
[1m[36mUser Load (1.4ms)[0m [1mSELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1[0m
Entered create method
[1m[35mUser Load (3.9ms)[0m SELECT "users".* FROM "users" WHERE "users"."email" = 'my#mail.com' LIMIT 1
Completed 200 OK in 99ms (Views: 0.3ms | ActiveRecord: 5.3ms)
Then, when I run $ curl -X POST "https://localhost:3000/api/v1/tokens.json?email=my#mail.com&password=somepassword" -d "email=my#mail.com&password=somepassword" -v -k -i, I get
Started POST "/api/v1/tokens.json?email=my#mail.com&password=[FILTERED]" for 127.0.0.1 at 2013-11-25 11:29:01 +0100
Processing by Api::V1::TokensController#create as JSON
Parameters: {"email"=>"my#mail.com", "password"=>"[FILTERED]"}
Completed 401 Unauthorized in 12ms
You might ask why I provided the parameters both as HTTP Post data, and as a query string. Well, my initial research with the curl lib sample http-post.c suggest the former, while Postmans successful query suggests the latter. I've tried all combinations of these, but nothing works, so I'm pretty lost.
In the Api::V1::TokensController, I've added logging whenever the create method is being called, that is
class Api::V1::TokensController < ApplicationController
skip_before_filter :verify_authenticity_token
respond_to :json
def create
Rails.logger.debug("Entered create method")
email = params[:email]
password = params[:password]
if request.format != :json
render :status=>406, :json=>{:message=>"The request must be json"}
return
end
if email.nil? or password.nil?
render :status=>400,
:json=>{:message=>"The request must contain the user email and password."}
return
end
#user=User.find_by_email(email.downcase)
if #user.nil?
logger.info("User #{email} failed signin, user cannot be found.")
render :status=>401, :json=>{:message=>"Invalid email or password."}
return
end
# http://rdoc.info/github/plataformatec/devise/master/Devise/Models/TokenAuthenticatable
#user.ensure_authentication_token!
if not #user.valid_password?(password)
logger.info("User #{email} failed signin, password \"#{password}\" is invalid")
render :status=>401, :json=>{:message=>"Invalid email or password."}
else
render :status=>200, :json=>{:token=>#user.authentication_token}
end
end
def destroy
#user=User.find_by_authentication_token(params[:id])
if #user.nil?
logger.info("Token not found.")
render :status=>404, :json=>{:message=>"Invalid token."}
else
#user.reset_authentication_token!
render :status=>200, :json=>{:token=>params[:id]}
end
end
end
As can be seen from the logs, create method is being called in the first place, but not in the second. It is as if Api::V1::TokensController's skip_before_filter :verify_authenticity_token is ignored altogether. But how can that be?
Any suggestion is greatly appreciated!

So here's what I did. For the regular html views, I'd like to keep using cookies for user convenience. For the (mobile) REST API, I'd like to disable cookies, as auth_token will be used throughout.
I'm not a Rails/Devise super hero, so I'm a little shaky about this, but I think it should be ok ... anyway: I disabled authentication before hitting the tokens controller altogether. In order to get anything sensible out of the tokens controller, you'd have to supply a matching email/password credentials pair anyway, so I can't (at the moment) see any obvious security vulnerabilities with this approach.
Thus, my new TokensController look like this:
class Api::V1::TokensController < ApplicationController
skip_before_filter :verify_authenticity_token
skip_before_filter :authenticate_player!
respond_to :json
def create
# ....
Hope this helps someone else stuck out there. And any feedback on possible security issues is greatly appreciated!
Update:
I forgot to mention that, as of now, the entire application is communicated over HTTPS.

Related

Signing out with Devise - making an unnecessary DELETE request

I have a Rails API using Devise and a React frontend. I've made a button that when clicked uses axios to make a DELETE request to the /users/sign_out route (is this how I should be logging out with React & Devise?). When I click the button I get a 404 error on the client, and the below error on the server. It seems like the initial request succeeds, but then it attempts to make another DELETE request to the / route and the user isn't signed out properly. Why is this the case?
Started DELETE "/users/sign_out" for ::1 at 2020-08-04 22:59:19 -0700
Processing by Devise::SessionsController#destroy as HTML
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
Can't verify CSRF token authenticity.
Redirected to http://localhost:3000/
Completed 302 Found in 5ms (ActiveRecord: 0.4ms | Allocations: 2069)
Started DELETE "/" for ::1 at 2020-08-04 22:59:19 -0700
ActionController::RoutingError (No route matches [DELETE] "/"):
The issue over here is that the sign_out is actually not working properly. As you can see there is a message in the log regarding Can't verify CSRF token authenticity.. I have a hunch that if you sign out and then try to login without refresh then that wouldn't work either.
To fix this you can take one of these 2 steps:
Override Devise::SessionsController and skip the token check with skip_before_filter :verify_authenticity_token. You should normally see a Completed 204 No Content response on successful sign out.
Be aware, that this would skip the CSRF token check so only do it if you have a different way of authenticating the validity of the request for ex. jwt token
Option would be to just send the CSRF token with your request if you are actually using it.
This issue comes from the respond_to. More precisely, it comes from the method respond_to_on_destroy (Devise::SessionsController#respond_to_on_destroy)
# Devise::SessionsController
def respond_to_on_destroy
# We actually need to hardcode this as Rails default responder doesn't
# support returning empty response on GET request
respond_to do |format|
format.all { head :no_content }
format.any(*navigational_formats) { redirect_to after_sign_out_path_for(resource_name) }
end
end
The last line will redirect user to after_sign_out_path while according to the document:
If you are using XHR requests other than GET or POST and redirecting after the request then some browsers will follow the redirect using the original request method. This may lead to undesirable behavior such as a double DELETE. To work around this you can return a 303 See Other status code which will be followed using a GET request.
so I add status: :see_other after redirect_to:
# Devise::SessionsController
def respond_to_on_destroy
# We actually need to hardcode this as Rails default responder doesn't
# support returning empty response on GET request
respond_to do |format|
format.all { head :no_content }
format.any(*navigational_formats) { redirect_to after_sign_out_path_for(resource_name), status: :see_other }
end
end
Which helps me clear the wrong DELETE request.
refs:
Rails Redirect After Delete Using DELETE Instead of GET
How is it possible for a legitimate user to submit an invalid CSRF token in Rails?

redirecting to root rather than controller/edit for password reset

NOTE: I'm not using Devise.
I'm implementing a password reset action and I have the controller Password_Resets with the following edit action:
def edit
end
def update
puts "I'm in the update!!"
if params[:user][:password].empty?
#user.errors.add(:password, "can't be empty")
render 'edit'
elsif #user.update_attributes(user_params)
log_in #user
flash[:success] = "Password has been reset."
redirect_to recipes_url
else
render 'edit'
end
end
When I'm trying to run through it, I put in the appropriate URL and I get the following response:
Started GET "/password_resets/igArFj9sYLt1J6k6Y2BjSg/edit?email=maggie%40example.com" for ::1 at 2016-04-20 21:49:58 -0500
Processing by PasswordResetsController#edit as HTML
Parameters: {"email"=>"maggie#example.com", "id"=>"igArFj9sYLt1J6k6Y2BjSg"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT 1 [["email", "maggie#example.com"]]
Redirected to http://localhost:3000/
Filter chain halted as :valid_user rendered or redirected
Completed 302 Found in 2ms (ActiveRecord: 0.1ms)
Why is it redirecting to localhost rather than the view app/views/password_resets/edit?? I can't seem to figure out a good way to "debug" this or see what's going on.
NOTE: I made that view incredibly simple to make sure it wasn't redirecting.
This is the view app/views/password_resets/edit:
<h1>This is my edit view </h1>
EDIT::: SOLUTION
So I was just an idiot. Essentially I have a validated user command that checked if the user was authenticated and the user was not. Therefore I need some more error handling.
Thanks you!!
The error is in your validations:
Filter chain halted as :valid_user rendered or redirected
I'm assuming you are calling this at the top of your controller
You can see in your web server logs:
Filter chain halted as :valid_user rendered or redirected
It's mean you have before_filter or before_action in a controller.
This filter is just ruby method - def valid_user and obvious that it contains a condition with redirect.
You need to debug this method.
You can read more about controller filters here

HTTP status code 302

Im working on my Rails Backend in Ruby and i want to post Data to this server. But if i do a Post-request with PAW i get redirected. Im a newbie to Http Requests. Could someone explain me the functionality and how to use http post requests?
i want to post information on my server's datanase (sqlite3).
Here's a screenshot which should explain everything:
how does this work? please explain :)
thanks.
greetings John
and here's the code:
OwnersController:
#app/controllers/owners_controller.rb
class OwnersController < SessionsController
respond_to :html
before_action :owner_find, only: [:show, :edit, :update, :destroy]
def index
#owners = Owner.all
end
def show
end
def update
#owner = Owner.find(params[:id])
if #owner.update(owner_params)
redirect_to #owner
else
render 'edit'
end
end
def new
#owner = Owner.new
end
def destroy
#owner.destroy
redirect_to owners_path
end
def edit
end
def create
#owner = Owner.new owner_params
if #owner.save!
flash[:notice] = 'You signed up successfully'
flash[:color]= 'valid'
redirect_to owners_path
else
flash[:notice] = 'Form is invalid'
flash[:color]= 'invalid'
render 'new'
end
end
private
def owner_find
#owner = Owner.find(params[:id])
end
def owner_params
params.require(:owner).permit(:name, :password, :password_confirmation, :token)
end
end
SessionController:
class SessionsController < ApplicationController
before_filter :authenticate_user, :except => [:login, :login_attempt]
def login
#goes to Login Form
end
def logout
session[:owner_id] = nil
redirect_to :action => 'login'
end
def login_attempt
authorized_user = Owner.authenticate_by_name(params[:login_name],params[:login_password])
if authorized_user
session[:owner_id] = authorized_user.id
flash[:notice] = "Wow Welcome again, you logged in as #{authorized_user.name}"
redirect_to welcome_index_path
else
flash[:notice] = 'Invalid Username or Password'
flash[:color]= 'invalid'
render 'login'
end
end
end
Console Logs:
from web-request (http://192.168.2.144:3000/owners?name=hans&password=hans321&password_confirmation=hans321)
Started GET "/owners?name=hans&password=[FILTERED]&password_confirmation=[FILTERED]" for 192.168.2.144 at 2015-10-01 12:12:18 +0200
Cannot render console from 192.168.2.144! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by OwnersController#index as HTML
Parameters: {"name"=>"hans", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}
Owner Load (0.1ms) SELECT "owners".* FROM "owners" WHERE "owners"."id" = ? LIMIT 1 [["id", 2]]
Owner Load (0.1ms) SELECT "owners".* FROM "owners"
Rendered owners/index.html.erb within layouts/application (1.8ms)
Completed 200 OK in 60ms (Views: 58.9ms | ActiveRecord: 0.2ms)
It's telling 200 ok but nothing happens in the DB.
from Paw-Request (so i can use post. btw. how do i use post in browser request?
Started POST
"/owners?name=hans&password=[FILTERED]&password_confirmation=[FILTERED]"
for 192.168.2.144 at 2015-10-01 12:12:45 +0200 Cannot render console
from 192.168.2.144! Allowed networks: 127.0.0.1, ::1,
127.0.0.0/127.255.255.255 Processing by OwnersController#create as HTML Parameters: {"name"=>"hans", "password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]"} Can't verify CSRF token
authenticity Redirected to http://192.168.2.144:3000/ Filter chain
halted as :authenticate_user rendered or redirected Completed 302
Found in 1ms (ActiveRecord: 0.0ms)
It seems that the CRSF authentication failed..
Edit:
at first:
to Rich Peck! This helped me so much. Thank you!! I really appreciate your effort.
Im near to the solution.. My problem is: i cant put the correct params in the url. The token-auth is disabled for testing. so it wont matter.
the params should be like:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"q9JvFhoSUgfydFTvh18JHbIIdKNDjnOS9m/trVBu9EHPP04xGsO69zPh1BFZBI1Ev1YcnOTiPmaAiPWOSkm5Xg==", "owner"=>{"name"=>"Hubert", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Create Owner"}
and not as in my request:
Parameters: {"name"=>"Hubert", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "owner"=>{}}
HTTP Status Codes
Firstly, a 30x response means "Resource Moved".
301 responses are used by many SEO people to denote permanent relocation of resources. 302 not so common, but still means a similar thing.
Every time you send & receive HTTP requests, you're going to receive a status code. The typical is the 200 response -- status success!
What you're seeing is the redirect_to command in action -
if #owner.save!
flash[:notice] = ...
redirect_to owners_path
I've never used PAW before, but I assume it's just giving you the pure response of the server, which would in this case be a 30x "Resource Moved" code.
I would expect a typical browser request to load the redirected route and display its yield on the screen.
Server
As a way to test this, you should attempt the same transaction in your browser:
lvh.me:3000/orders
(lvh.me is a domain routed to your own localhost which helps with subdomains in Rails)
This will give you the ability to test and see what happens with the responses. You *should * find that your data has been saved to the database (albeit SQLite3 in your case).
Syntax
Finally, you need to ensure you're using the correct syntax in your code.
Specifically:
#app/controllers/owners_controller.rb
class OwnersController < ApplicationController
...
def create
#owner = Owner.new owner_params
end
private
def owner_params
params.require(:owner).permit(:name, :password, :password_confirmation)
end
end
You'll also want to look at bcrypt-ruby for protecting your passwords.
Testing
I tend to just test my Rails apps with standard browser functionality.
This means you can run the Rails Server ($ rails s in your console), which you'll then be able to then access through your browser.
You're trying to use this PAW thing, which is okay, but doesn't give you much flexibility in regard to the user-interactivity of the app (for example, submitting real forms etc)...
In your case, I'd do the following:
#app/views/orders/new.html.erb
<%= form_for #order do |f| %>
<%= f.text_field :name %>
<%= f.password_field :password %>
<%= f.password_field :password_confirmation %>
<%= f.submit %>
<% end %>
You'd then access lvh.me:3000/orders/new and submit the form. This will show you how it responds!
HTTP
Okay here's the deal with HTTP requests...
Whenever you send a piece of transactional data to your web application, you do it through an HTTP request. HTTP requests are just a way to send data through the "Internet".
With Rails based apps, this means that every time you "do" something in the app, you're really sending an HTTP request to your web server. Rails interprets this request and sends a response. This response is what your question is about.
You're asking about receiving 302 responses - this is the web server's way of saying you've been redirected. It's pretty basic stuff to be honest; your browser handles most of it.
A great tutorial can be found here:
Alright then your error is as follows:
Can't verify CSRF token authenticity
I can elaborate more on this later, but for now, you might want to look up this solution: WARNING: Can't verify CSRF token authenticity in case of API development

Rails_Admin Sign out not Redirecting (Devise)

We are using Devise, Rails_Admin and Simple_Token_Authentication (for API) in our application.
Everything is working fine except for sign out. When we click on Sign out, following request is made and the user gets signed out.
Started DELETE "/admins/sign_out" for 127.0.0.1 at 2015-07-12 18:50:44
+0530 Processing by Devise::SessionsController#destroy as HTML Parameters:
{"authenticity_token"=>"rtSRPzpRN7cWEk8wV8q6VDAUB575ZV46JeFFlMFVOQc="}
Admin Load (0.4ms) SELECT "admins".* FROM "admins" WHERE
"admins"."id" = 1 ORDER BY "admins"."id" ASC LIMIT 1 (0.1ms)
begin transaction (0.1ms) commit transaction Completed 204 No
Content in 700ms (ActiveRecord: 0.5ms)
The problem is that the page does not redirect after sign out.
On pressing the sign out button again, here is the request:
Started DELETE "/admins/sign_out" for 127.0.0.1 at 2015-07-12 19:01:59
+0530 Processing by Devise::SessionsController#destroy as HTML Parameters:
{"authenticity_token"=>"dHuxA5hRosyquhlsRmchK3vW9bQOCM/YXYXUNMxTufc="}
Filter chain halted as :verify_signed_out_user rendered or redirected
Completed 204 No Content in 1ms (ActiveRecord: 0.0ms)
Here is the application controller (app/controllers/application_controller.rb):
class ApplicationController < ActionController::Base
protect_from_forgery
skip_before_filter :verify_signed_out_user
respond_to :html, :json
protected
# Overwriting the sign_out redirect path method
def after_sign_out_path_for(resource_or_scope)
request.referrer
end
end
Here is the devise related code in app/config/initializers/rails_admin.rb
config.authenticate_with do
warden.authenticate! scope: :admin
end
config.current_user_method(&:current_admin)
Please suggest. Thanks in advance!
The problem is in your after_sign_out_path_for(resource_or_scope).
request.referrer redirects to the current page.
Change the after_sign_out_path_for(resource_or_scope) accordingly. If you want to redirect to root_path, then below would work.
def after_sign_out_path_for(resource_or_scope)
root_path
end

Using a curl command to login to rails api with devise

I have a simple web app built with rails using devise for user log in. At the moment a user can sign up, sign in and sign out, and make new posts when signed in.
Eventually I plan to make this api consumable via a mobile app and my first step in understanding the process is to use a CURL command to log in. I am having some problems.
Here is the CURL command:
curl -H 'Content-Type: application/json' -X POST http://localhost:3000/users/sign_in -d '{"user": {"email":"test#test.com", "password": "password"}}' -c cookie
Here is the controller code:
class Api::PostsController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :authenticate_user!
# protect_from_forgery with: :null_session
def index
render json: Post.all
end
def show
post = Post.find(params[:id])
render json: post
end
def create
post = Post.new(post_params)
if post.save
render status: 200, json: {
message: "Successfully created post",
post: post
}.to_json
else
render status: 422, json: {
errors: post.errors
}.to_json
end
end
def destroy
post = Post.find(params[:id])
post.destroy
render status: 200, json: {
message: 'Successfully deleted post'
}.to_json
end
private
def post_params
params.require("post").permit("city", "country", "image", "lon", "lat")
end
end
Here is the error I am getting from the server:
Processing by Devise::SessionsController#create as */*
Parameters: {"user"=>{"email"=>"test#test.com", "password"=>"[FILTERED]"}, "session"=>{"user"=>{"email"=>"test#test.com", "password"=>"[FILTERED]"}}}
Can't verify CSRF token authenticity
Completed 422 Unprocessable Entity in 20ms (ActiveRecord: 0.0ms)
As you can see I've included the skip_before_action :verify_authenticity_token code to skip the CSRF token check, however this check seems to be taking place in the SessionsController.
Do I need to add this line of code elsewhere? I am still new to Rails/Devise, so please forgive my lack of knowledge.
Devise is a Rails engine, so it has its own set of controllers which do not inherit from ApplicationController. To make the override work, you need to make controllers in your app that inherit from the devise ones, then tell Devise to use those instead of the default ones. This how-to on the Devise wiki might help.
Having said that, the CSRF protection is there for a reason, and if you disable it, the users logging in via the web browser will no longer be protected, which is a really bad idea. A better way to do this would be to have a proper mechanism for logging in via the API which is different from the web GUI. There is a :token_authenticatable module that will help here, and a good guide to using it here.
However... the Platformatec guys who make Devise have removed :token_authenticatable from the core as they say it is not 100% secure (see this blog post). There is a good Stackoverflow question here explaining the issues and suggesting some better ways to implement things.
Hope that throws some light on things.

Resources