I have this very simple Ruby code which makes Ruby POST request.
For some reason it creates 2 requests - one authentication error and one successful request:
def execute
request = HTTPClient.new()
request.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
request.set_auth('https://test.net/api', 'user', 'pass')
request_body = File.read("requests/xml/req.xml")
response = request.post('https://test.net/api', request_body, {'Content-Type' => 'application/xml', 'cache-control' => 'no-cache'}).body
puts 'called'
end
I suppose that some configuration if missing for HTTPClient?
Can you advise how I can fix this?
This is an expected behavior. The client won't send the credentials unless they are required. When the client gets a 401 status code then it sends the request again but now with the credentials.
You can force some clients to send the credentials on the first request.
Ruby's HTTPClient has a force_basic_auth that does that.
Related
Now I took a sample code of Twitter v2 API from this link. This sample code shows how OAuth and twitter v2 API work for positng a tweet. It works fine with my consumer key and consumer secret.
And I want to simplify the code like below. It assumes that the access token and access token secret are already known and it skips the process of user's approval, like providing the URL that provides PIN.
require 'typhoeus'
require 'json'
consumer_key = CONSUMER_KEY
consumer_secret = CONSUMER_SECRET
token = ACCESS_TOKEN
token_secret = ACCESS_TOKEN_SECRET
consumer = OAuth::Consumer.new(consumer_key, consumer_secret, :site => 'https://api.twitter.com')
options = {
:method => :post,
headers: {
"User-Agent": "v2CreateTweetRuby",
"content-type": "application/json"
},
body: JSON.dump("Hello, world!")
}
create_tweet_url = "https://api.twitter.com/2/tweets"
request = Typhoeus::Request.new(create_tweet_url, options)
access_token = OAuth::Token.new(token, token_secret)
oauth_params = {:consumer => consumer, :token => access_token}
oauth_helper = OAuth::Client::Helper.new(request, oauth_params.merge(:request_uri => create_tweet_url))
request.options[:headers].merge!({"Authorization" => oauth_helper.header}) # Signs the request
response = request.run
puts response
Then, I see the below error message.
ruby test_tweet.rb
/usr/local/lib/ruby/gems/3.1.0/gems/oauth-0.5.10/lib/oauth/request_proxy.rb:18:in `proxy': Typhoeus::Request (OAuth::RequestProxy::UnknownRequestType)
from /usr/local/lib/ruby/gems/3.1.0/gems/oauth-0.5.10/lib/oauth/signature.rb:12:in `build'
from /usr/local/lib/ruby/gems/3.1.0/gems/oauth-0.5.10/lib/oauth/signature.rb:23:in `sign'
from /usr/local/lib/ruby/gems/3.1.0/gems/oauth-0.5.10/lib/oauth/client/helper.rb:49:in `signature'
from /usr/local/lib/ruby/gems/3.1.0/gems/oauth-0.5.10/lib/oauth/client/helper.rb:82:in `header'
from test_tweet.rb:28:in `<main>'
When I used irb and tried step by step, this error happens at oauth_helper.header. As this is the first time to use OAuth API, I may be making some easy mistakes. Does anybody find anything wrong in my code?
I confirmed that my access token and access token secret work at https://web.postman.co/.
Thanks.
You need to insert
require 'oauth/request_proxy/typhoeus_request'
and your code may complete task you desire.
Other lines looks good to me!
In oauth/request_proxy.rb, oauth library check class of request object.
https://github.com/oauth-xx/oauth-ruby/blob/master/lib/oauth/request_proxy.rb
return request if request.is_a?(OAuth::RequestProxy::Base)
klass = available_proxies[request.class]
# Search for possible superclass matches.
if klass.nil?
request_parent = available_proxies.keys.find { |rc| request.is_a?(rc) }
klass = available_proxies[request_parent]
end
raise UnknownRequestType, request.class.to_s unless klass
By requiring 'oauth/request_proxy/typhoeus_request', Typhoeus::Request inherits OAuth::RequestProxy::Base and raising UnknownRequestType error can be avoided.
https://github.com/oauth-xx/oauth-ruby/blob/master/lib/oauth/request_proxy/typhoeus_request.rb
I'm currently using Chef to build out a cookbook that has to fire off a bunch of POST calls to this API and I have to capture the response in a variable to use it in a second HTTP call.
I've tried using the http_request resource from Ruby but I can only fire the call but don't know how to get the response captured:
http_request 'authorize' do
action :post
url '*****************************' headers ({
'Content-Type' => 'application/json'
}) message ({
:Username => "**********",
:Password => "**********"
}).to_json
end
In another attempt, I tried using Chef's http client to fire off a POST call and a get a response:
require "net/https"
require "uri"
require "json"
uri = URI("******************************")
req = Net::HTTP::Post.new(uri)
req.set_form_data("Username" => "********", "Password" => "*********")
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
http.request(req)
end
case res
when Net::HTTPSuccess, Net::HTTPRedirection
# OK
else
res.value
end
But I keep getting this error when I run the chef-client on my node:
EOFError
--------
end of file reached
How can I send off a POST call using Chef/Ruby and capture its response?
You want to use the Chef::HTTP client class, see https://coderanger.net/chef-tips/#4 for an example.
In my case the request url had https so I had to add this line and it was working.
http.use_ssl = true
If you want a more generic approach that can handle both http and https see this answer -
https://stackoverflow.com/a/9227933/6226283
I am using Faye websocket implementation. When a websocket is detected I want to make sure the user sent a given header before I open the connection, or else I want to return an error.
I tried returning error 401 using [] but I keep oberving that the code continues execution and the websocket is created anyway. I managed to prevent it by adding return after it, but I am not sure this is the right way. On the client side I am implementing it in python using ws4py and when I return I get an exception raised indicating it received a 302 code, not the 401 I was expecting to send.
My ruby code (without the websocket events code) follows:
App = lambda do |env|
if Faye::WebSocket.websocket?(env)
unless env['HTTP_MY_HEADER']
[401, {'Content-Type' => 'application/json'}, '{"message": "I was expecting a header here"}']
#return (??)
end
ws = Faye::WebSocket.new(env)
# Rest of websocket call handling
else
# Normal HTTP request
[200, {'Content-Type' => 'text/plain'}, ['Hello']]
end
end
I intend to send GET request with net/http just like that:
require 'net/http'
response = Net::HTTP.get_response("example.com", "/whoareyou.json")
but I need to include key and secret in the url. I tried to make it like this:
require 'net/http'
response = Net::HTTP.get_response("<key>:<secret>#example.com", "/whoareyou.json")
But unfortunately I'm getting nasty error:
/usr/lib/ruby/1.9.1/net/http.rb:762:in `initialize': getaddrinfo: Name or service not known (SocketError)
Net::HTTP does not support basic authentification from the URL. You need to call req.basic_auth 'user', 'pass'
http://ruby-doc.org/stdlib-2.1.3/libdoc/net/http/rdoc/Net/HTTP.html#class-Net::HTTP-label-Basic+Authentication
I suggest also tu use the HTTP Party gem ( https://github.com/jnunemaker/httparty ) that really simplify most of the HTTP stuff , see this answer for instance to see how basic auth can be used with HTTP party : How to use basic authentication with httparty in a Rails app?
In your example:
res = HTTParty.get("http:example.com/whoareyou.json", :basic_auth=> {:username =>user, :password => password})
:basic_auth => auth)
I am trying to implement facebook authentication for an app with warden, after the user allows facebook auth and redirects to my app callback with the token I get a 400 while consuming the api. My warden strategy is this:
class Facebook < Warden::Strategies::Base
def client
#client ||= OAuth2::Client.new MyApp::Facebook::AppID, MyApp::Facebook::AppSecret, :site => 'https://graph.facebook.com'
end
def params
#params ||= Rack::Utils.parse_query(request.query_string)
end
def authorize_url
client.web_server.authorize_url :redirect_uri => request.url, :scope => 'email,publish_stream'
end
def authenticate!
throw(:halt, [302, {'Location' => authorize_url}, []]) unless params['code']
facebook = client.web_server.get_access_token params['code'], :redirect_uri => request.url
rescue OAuth2::HTTPError => e
puts e.response.body
end
end
Strategies.add :facebook, Facebook
The result of printing the response body is this:
{"error":{"type":"OAuthException","message":"Error validating client secret."}}
I am pretty shure the app id and app secret are the ones provided by FB.
Thanks.
I've seen that error message many times. Here are the things I would double check:
your domain is the same as what you listed in the facebook callback url
the app id is correct (actually print this out on a page, sometimes y
the app secret is correct
Add redirect_uri while creating the object of facebook that will fix the issue.
Redirect the user to https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&redirect_uri=YOUR_URL
After user click allow, it'll hit our Redirect Uri
At that point we'll get the code and we need to do a server side HTTP Get to the following Url to exchange the code with our oAuth access token:
https://graph.facebook.com/oauth/access_token?
client_id=YOUR_APP_ID&redirect_uri=YOUR_URL&
client_secret=YOUR_APP_SECRET&code=THE_CODE_FROM_ABOVE
Now at step 3, I kept on getting Http 400 response back.
So after some research, I found out that on that redirect_uri that we submitted on step 3 doesn't do anything but validate the request. Thus, the value need to match with step 2.
I also get the same error and i resolved by doing as below:
double check your client_id, client_secret, redirect_uri.
Add Accept: "application/json" header to thye request
fetch(
`https://graph.facebook.com/v15.0/oauth/access_token?client_id=${process.env.FACEBOOK_APP_ID}&redirect_uri=${process.env.FACEBOOK_REDIRECT_URI}&client_secret=${process.env.FACEBOOK_APP_SECRET}&code=${code}`,
{
method: "GET",
headers: {
Accept: "application/json",
},
}
)