Webhook returning 401 in Rails - ruby-on-rails

I have created a webhook using Fluidsurveys which corresponds to this controller:
class HooksController < ApplicationController
skip_before_filter :verify_authenticity_token
def response_created_callback
# If the body contains the score parameter...
if params[:_completed].present?
# Create a new Response object based on the received parameters...
response = FluidsurveysResponse.new(:completed => params[:_completed])
response.invite_email = params[:_invite_email]
response.locale = params[:_locale]
response.key = params[:_key]
response.webhook = params[:webhook]
response.survey_name = params[:survey_name]
response.username = params[:_username]
response.weighted_score = params[:_weighted_score]
response.completion_time = params[:_completion_time]
response.ip_address = params[:_ip_address]
response.num_responses = params[:num_responses]
response.survey_id = params[:survey_id]
response.invite_name = params[:_invite_name]
response.language = params[:_language]
response.referrer = params[:_referrer]
response.fs_created_at = params[:_created_at]
response.fs_updated_at = params[:_updated_at]
response.survey_url = params[:survey_url]
response.fs_id = params[:_id]
response.collector_id = params[:_collector_id]
response.comment = params[:Comment]
response.nps_score = params[:NPSScore]
response.save!
end
# The webhook doesn't require a response but let's make sure
# we don't send anything
render :nothing => true
end
end
The webhook seems to work fine, as I see this in my logs:
2014-01-20T13:49:01.231772+00:00 app[web.1]: Started POST "/hooks/response_created_callback" for 184.107.171.218 at 2014-01-20 13:49:01 +0000
2014-01-20T13:49:01.327989+00:00 app[web.1]: Processing by HooksController#response_created_callback as */*
2014-01-20T13:49:01.328149+00:00 app[web.1]: Parameters: {"_invite_email"=>"N/A", "_locale"=>"298", "_updated_at"=>"2014-01-20 13:49:00.738850", "_language"=>"en", "_key"=>"b5ec09680a4274cec0052d4049bec338a906e5b8", "webhook"=>"event", "survey_name"=>"Test", "_referrer"=>"http://fluidsurveys.com/surveys/marketing/test-nps-for-dc/", "_username"=>"marketing", "survey_url"=>"http://fluidsurveys.com/surveys/marketing/test-nps-for-dc/", "_id"=>"39164209", "_created_at"=>"2014-01-20 13:49:00.243640", "_weighted_score"=>"0.0", "_completion_time"=>"00:00:00", "_completed"=>"1", "_ip_address"=>"66.192.31.1", "yptmAoJw6i"=>"Detractor: 0", "num_responses"=>"261", "survey_id"=>"263692", "_extra_info"=>"weighted_score", "_invite_name"=>"N/A"}
2014-01-20T13:49:01.369963+00:00 app[web.1]: Completed 401 Unauthorized in 41ms
As you can see, the post gets made to the right controller action. The route is setup correctly, but I get a 401 error. I don't understand what authentication needs to happen here...The params are getting passed to the controller, and in my mind I see no need for my app to authenticate anything. It receives a request, does what it's told, and then it's done.
What am I missing here that is causing the 401 error?
EDIT:
After remove skip_before_filter :verify_authenticity_token from my controller, I get this error:
Can't verify CSRF token authenticity
2014-01-20T16:07:12.222512+00:00 app[web.1]: Completed 422 Unprocessable Entity in 28ms
2014-01-20T16:07:12.225905+00:00 app[web.1]:
2014-01-20T16:07:12.225905+00:00 app[web.1]: ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):

The issue is that I am using devise for user authentication, and my app was blocking the post request because there were no authentication credentials being passed to my controller.
By adding this line to my controller I solved the issue:
skip_before_filter :verify_authenticity_token, :authenticate_user!

Related

Twilio Webhooks in Rails - 401 error - no lines run in method?

I am trying to create a record in a model based on a incoming SMS. This is all in development now. I have the webhooks setup so that the incoming message triggers a webhook which then is processed by the controller. However, the method in the controller doesn't do anything and in the cmd line I get a 401 error.
Here is the code from the webhook controller and the error message:
class TwilioController < ApplicationController
skip_before_action :verify_authenticity_token
def sms_received
#daily_journal = DailyJournal.new
#daily_journal.user_id = current_user.id
#daily_journal.message_sid = params["MessageSid"]
#daily_journal.entry_text = params["Body"]
#daily_journal.save
end
Started POST "/twilio/sms_received" for 54.81.71.35 at 2022-12-04 20:36:57 +0200
Cannot render console from 54.81.71.35! Allowed networks: 127.0.0.0/127.255.255.255, ::1
Processing by TwilioController#sms_received as */*
Parameters: {"ToCountry"=>"US", "ToState"=>"TX", "SmsMessageSid"=>"SMa57a8f9422708b4c3336d33f9be17179", "NumMedia"=>"0", "ToCity"=>"ARLINGTON", "FromZip"=>"22191", "SmsSid"=>"SMa57a8f9422708b4c3336d33f9be17179", "FromState"=>"VA", "SmsStatus"=>"received", "FromCity"=>"ARLINGTON", "Body"=>"R", "FromCountry"=>"US", "To"=>"+18171234567", "MessagingServiceSid"=>"MG4b459339d046009b64e02ad50becab05", "ToZip"=>"76012", "NumSegments"=>"1", "ReferralNumMedia"=>"0", "MessageSid"=>"SMa57a8f9422708b4c3336d33f9be17179", "AccountSid"=>"deleted_but_there_was_something_here", "From"=>"+17031234567", "ApiVersion"=>"2010-04-01"}
Completed 401 Unauthorized in 7ms (Allocations: 690)

Confusion between url_for and host definition for ActionMailer

Rails mailer instances do not have any context about the request. However this is needed to service multiple hosts. I was hoping to invoke url_for for the mailer, but I am confused to both the placing of it AND how it should be constructed (the api documentation provides examples for controllers, not mailers).
The form submission has a hidden_field :host, value: #site.host which percolates to the request
Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"email"=>"some#quack.com", "host"=>"localhost"}
environments/development.rb has config.action_mailer.default_url_options commented out.
initializer for devise has config.parent_mailer = 'DeviseMailer'
The users/passwords_controller.rb has been edited
class Users::PasswordsController < Devise::PasswordsController
def create
puts params[:host]
super
end
and mailers/devise_mailer.rb sets
class DeviseMailer < ActionMailer::Base
layout 'mailer'
before_action :set_mailers_url_host
def set_mailers_url_host
puts 'host'
puts params
ActionMailer::Base.default_url_options[:host] = params[:user][:host]
end
I did not expect the mailer to know the parameter as it is designed to inherit from < ActionMailer::Base
However, the log is indicating that the password controller generated is not being invoked. the 'host' string is being put, then an empty line indicates the mailer know nothing of the params
Processing by Devise::PasswordsController#create as HTML
[...]
↳ app/controllers/application_controller.rb:284:in `get_departments'
User Load (2.5ms) SELECT "users"...
User Load (1.2ms) SELECT "users"...
TRANSACTION (1.0ms) BEGIN
User Update (1.7ms) UPDATE "users" SET "reset_password_token" = $1, "reset_password_sent_at" = $2 WHERE "users"."id" = $3 [["reset_password_token", "..."], ["reset_password_sent_at", "..."], ["id", 45]]
TRANSACTION (6.1ms) COMMIT
host
Devise::Mailer#reset_password_instructions: processed outbound mail in 0.6ms
Completed 500 Internal Server Error in 745ms (ActiveRecord: 81.6ms | Allocations: 203970)
NoMethodError (undefined method `[]' for nil:NilClass):
app/mailers/devise_mailer.rb:8:in `set_mailers_url_host'
The error is expected given params[:user][:host] is an unknown entity to the mailer. The bypassing of the passwords controller, not.
Also attempted: commenting out the devise_mailer before_action and adding to the application_controller.rb, where #site is set in before_action :set_site :
def default_url_options
{ host: #site.host, locale: I18n.locale }
end
While this is the most succinct way of dealing with the case, that fails with error ActionView::Template::Error (Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true)
Why are the checks (via puts) in the passwords controller not being invoked?
How should url_for thus establish the required string based on params[:user][:site]
This is a way to solve your issue but I'm not quite satisfied with the beauty of this. 🤔
around_action :change_host
def change_host
default_options = Rails.application.routes.default_url_options
new_options = { host: param[:user][:host], port: :thing }
Rails.application.routes.default_url_options = new_options
Rails.application.configuration.action_mailer.default_url_options = new_options
yield
Rails.application.routes.default_url_options = default_options
Rails.application.configuration.action_mailer.default_url_options = default_options
end
Given all the moving parts, a quick review:
comment out config.action_mailer.default_url_options
submission of param in form for host
no need for DeviseMailer class
setting default_url_options in application_controller not necessary
url_for is uncalled for
First element of answer: get the routing established correctly:
devise_for :users, controllers: { passwords: 'users/passwords' }
Second element of solution (preliminary - works in development on remote server), modify the devise passwords controller.
def create
ActionMailer::Base.default_url_options[:host] = params[:user][:host]
super
end

401 Unauthorized error when making requests to rails api on heroku

I just deployed my first app to heroku and it used a Rails API (backend) and a React frontend. When I deployed my app I get 401 unauthorized errors whenever I try to make a request that involves the use of a JSON Web Token(JWT). I am sending the token from localstorage in my app and everything worked fine when I was sending it in my development environment. I only have this issue in production.
When I make the fetch request from my frontend and send over my JWT to my backend, I get the following messages in my heroku server logs:
2020-11-29T04:45:31.742735+00:00 app[web.1]: I, [2020-11-29T04:45:31.742670 #4] INFO -- : [f3c19eae-e431-4c9f-b93b-7499797f2c03] [active_model_serializers] Rendered ActiveModel::Serializer::Null with Hash (0.13ms)
2020-11-29T04:45:31.742984+00:00 app[web.1]: I, [2020-11-29T04:45:31.742919 #4] INFO -- : [f3c19eae-e431-4c9f-b93b-7499797f2c03] Filter chain halted as :authorized rendered or redirected
2020-11-29T04:45:31.744091+00:00 app[web.1]: I, [2020-11-29T04:45:31.744019 #4] INFO -- : [f3c19eae-e431-4c9f-b93b-7499797f2c03] Completed 401 Unauthorized in 2ms (Views: 0.7ms | Allocations: 218)
Another strange thing about this is that I get an error message along with the 401 status error in my React frontend that tells me to Please Log in even though I logged into my app to receive a token from my backend before I even attempted to make another fetch request
Below I will post other relevant code snippets so that you can see what I was trying to do
Here is the Code in my frontend that sends the request
addToCart = () =>{
//make a fetch request to add the item in the customer's current cart
fetch("https://health-and-fit-store-api.herokuapp.com/cart_products",{
method:"POST",
headers:{
"Authorization": localStorage.token,
"Content-Type":"application/json",
Accept:"application/json"
},
body:JSON.stringify({
productId :this.props.id
})
})
.then(res => res.json())
.then(data => {
this.props.addToCart(data)
toast.dark(`Added ${data.product.name.toLowerCase()} to your cart`)
})
}
Here is the code in my Rails API that receives the request
before_action :authorized, only: [:create,:delete]
def create
#will be receiving token in fetch request
#use the of the current cart and the product id passed in by the post request
current_cart = #customer.carts.find_by(checked_out:false)
product = Product.find(params[:productId])
new_cart_product = CartProduct.create(cart:current_cart,product:product, quantity:1)
render json: new_cart_product
end
Here is the code from my application controller that I used to set up JWT
class ApplicationController < ActionController::API
def encode_token(payload)
# should store secret in env variable
JWT.encode(payload, ENV['jwt_encode_string'])
#byebug
end
def auth_header
# { Authorization: 'Bearer <token>' }
request.headers['Authorization']
end
def decoded_token
if auth_header
token = auth_header
# header: { 'Authorization': '<token>' }
begin
JWT.decode(token, ENV['jwt_encode_string'], true, algorithm: 'HS256')
rescue JWT::DecodeError
nil
end
end
end
def logged_in_customer
if decoded_token
customer_id = decoded_token[0]['customer_id']
#customer = Customer.find_by(id: customer_id)
end
end
def logged_in?
!!logged_in_customer
end
def authorized
render json: { error_message: 'Please log in' }, status: :unauthorized unless logged_in?
end
end
If anyone can help me out with this, I would really appreciate it, I've been stuck on this for days. Also please note that I have checked out every other post involving this issue on StackOverflow and have exhausted every kind of search on Google that I could think of.
thanks to #snake, I actually ended up solving this issue. The problem wasn't with the token I was using, but their suggestion led me to go back and check out my fetch requests to the api endpoint and I had an extra comma at the end of the request that was causing the 401 status code error.
Once I fixed that, everything worked beautifully.

Ruby On Rails authentication issue with http basic(the authentication process is triggered twice for every request)

This is the request I send to the rails controller:
function login(){
$.ajax({
type: 'GET',
url: '/api/myapi/show',
username: 'username',
password: 'password',
contentType: "application/json; charset=utf-8",
success: function(data){
console.log(data);
},
error: function(xhr, ajaxOptions, thrownError) {
console.log(arguments);
}
});
}
login function is used as follows:
<body onload='login();'>
This is the controller:
class Api::MyApi::BaseController < ApplicationController
before_filter :authenticate
attr_reader :user
def authenticate
authenticate_or_request_with_http_basic do |username, password|
#authenticate method checks if user with username and password exists in database
#user = User.authenticate(username, password)
end
end
end
When I send the request, this is what's printed in the terminal:
Started GET "/api/myapi/show" for 127.0.0.1 at 2015-12-15 09:42:22 +0100
Processing by Api::MyApi#show as JSON
Parameters: {"id"=>"show", "test"=>{}}
Filter chain halted as :authenticate rendered or redirected
Completed 401 Unauthorized in 0ms (ActiveRecord: 0.0ms)
Started GET "/api/myapi/show" for 127.0.0.1 at 2015-12-15 09:42:22 +0100
Processing by Api::MyApi#show as JSON
Parameters: {"id"=>"show", "test"=>{}}
User Load (0.1ms) SELECT `users`.* FROM `users` WHERE `users`.`authorized` = 1 AND `users`.`verification_approved` = 1 AND `users`.`login` = 'user_login' LIMIT 1
Location Load (0.1ms) SELECT `locations`.* FROM `locations` WHERE `locations`.`id` = 9999 LIMIT 1
Rendered api/myapi/show.json.rabl (0.5ms)
Completed 200 OK in 8ms (Views: 2.6ms | ActiveRecord: 0.7ms)
As you can see, it tries to authenticate twice and fails the first time. It doesn't even get inside "authenticate_or_request_with_http_basic" because if I write a print statement inside the "authenticate_or_request_with_http_basic", it doesn't get printed the first time(when authentication fails), but does get printed the second time.
Things I tried:
1) When Removing the before_filter completely and just authenticating in the show method, the issue doesn't occur anymore.
2) When keeping/using the before_filter but replacing the authenticate_or_request_with_http_basic with 'true' like this:
def authenticate
true
end
the issue doesn't occur either.
3) The issue doesn't occur when I send a request with python:
import requests
r = requests.get('URL_TO__RoR_Controller', auth=('username', 'password'))
print r.text
UPDATE:
This might be useful info: The request is sent every 10 seconds, and the credentials are sent with every request. Perhaps this has something to do with the issue.
You are sending username and password as params
AFAIK Basic auth works by setting the authorization headers
Use jQuery's beforeSend callback to add an HTTP header with the authentication information: http://api.jquery.com/jQuery.ajax/
beforeSend: function (xhr) {
xhr.setRequestHeader ("Authorization", "Basic " + btoa(username + ":" + password));
},
The btoa() method encodes a string in base-64.
In your controller you can check the headers with
request.env["HTTP_AUTHORIZATION"]
Let me know if this works for you.

NoMethodError users_url with devise (ajax)

I use devise 2.2.2 with rails 3.2.11
I use devise with ajax requests
I changed the following configuration in initializers/devise.rb
config.navigational_formats = [:json, :html]
config.http_authenticatable_on_xhr = false
when I submit an empty sign in request, I expect to get a json response with errors hash, but i get a 500 instead (see below for the trace) (it works fine with sign up request)
here are my routes (nothing special)
devise_for :users
the trace:
Started POST "/users/sign_in.json" for 127.0.0.1 at 2013-01-27 13:33:45 +0100
Processing by Devise::SessionsController#create as JSON
Parameters: {"user"=>{"email"=>"", "password"=>"[FILTERED]"}}
Completed 401 Unauthorized in 1ms
Processing by Devise::SessionsController#new as JSON
Parameters: {"user"=>{"email"=>"", "password"=>"[FILTERED]"}}
Completed 500 Internal Server Error in 40ms
NoMethodError (undefined method `users_url' for #<Devise::SessionsController:0x007fe88ddd9550>):
You are probably overriding after_sign_in_path_for and have a code path in there that returns nil.
This causes devise to fall back to its default behaviour and call users_url to get the path to redirect to.
Why do I think this? Because you are having the same error I had (and lost some hair over) and also this bug report contains the github usernames of many other people who have been humbled by this particular issue.

Resources