Making an API call over SSL in Ruby - ruby-on-rails

I have a method that works just fine for calls to non-ssl apis, but it gives me the following error response whenever I request https-only apis:
757: unexpected token at '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
Reason: You're speaking plain HTTP to an SSL-enabled server port.<br />
Instead use the HTTPS scheme to access this URL, please.<br />
</p>
</body></html>
The method is fairly straightforward:
def my_service_api_call(path = nil, query = nil)
my_service_api_key = ENV["MY_SERVICE_KEY"]
api_path = "#{path}/?api_key=#{my_service_api_key}&#{query}"
url = URI.parse(api_path)
req = Net::HTTP::Get.new(api_path)
res = Net::HTTP.start(url.host, url.port) { |http| http.request(req) }
JSON.parse(res.body)["results"]
end
This works great over http but fails over https-only. Is there an equivalent way to do HTTPS requests?

There's a more elegant way to reuse your instance of Net::HTTP and enable requests over HTTPS:
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # Sets the HTTPS verify mode
#data = http.get(uri.request_uri)
Notice the use of use_ssl. From the documentation:
Turn on/off SSL. This flag must be set before starting session. If you change use_ssl value after session started, a Net::HTTP object raises IOError.
Note also that the usage of VERIFY_NONE is controversial, since it doesn't force the validity of of certificates to be checked. For many applications and users, this will not bear any negative ramifications. In cases where certificate validity should be checked, this post suggests the following:
Securely transfer the correct certificate and update the default certificate store or set the ca file instead.

You need to set use_ssl to true:
like this:
http = Net::HTTP.new(uri.hostname, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
http.ssl_version = :SSLv3

Related

How can I force Rails to only use TLS over SSL?

We are being forced to only use TLS instead of SSL when sending a request to a server, but we don't know how to make sure that the request is being sent using TLS, and if it's not, we're not sure how to force Rails to do it.
Here is how we are sending the request:
uri = URI.parse("https://someurl.com")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(uri.path)
request.body = body
http.request(request)
try to add
http.ssl_options = OpenSSL::SSL::OP_NO_SSLv2 + OpenSSL::SSL::OP_NO_SSLv3 + OpenSSL::SSL::OP_NO_COMPRESSION

Getting Google Contacts in Rails 3 app after successful oauth2 authentication with devise

I've searched around a lot and seen various answers. Some seem like they may be outdated, and others use gems (omnicontacts) that I can't seem to get working with devise.
In my situation, I already get an oauth token from Google successfully. Now I'm trying to send a get request to the url given by Google to get all my contacts. I'm able to do this in Google's oauth2 playground, but not from my rails app.
Here is the relevant code:
require 'net/http'
require 'json'
class User < ActiveRecord::Base
def get_google_contacts(auth_token)
uri = URI.parse("https://www.google.com/m8/feeds/contacts/default/full?oauth_token=#{auth_token}&max-results=50000&alt=json")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # You should use VERIFY_PEER in production
request = Net::HTTP::Get.new(uri.request_uri)
response = http.request(request)
contacts = JSON.parse(response.body)
puts contacts
end
end
Notebooks Controller: this is where I want a user to be able to access their contacts from Google.
def show
#contacts = current_user.get_google_contacts(current_user.oauth_token)
end
Here is the error log I get in my local:
JSON::ParserError in NotebooksController#show
746: unexpected token at '<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Error
401
(Client Error)!!1</title>
<p><b>401.</b>
<ins>That's an error.</ins></p>
<p>There was an error in your request.
<ins>That's all we know.</ins></p></body></html>'
I think I may be using incompatible techniques to parse the xml I get from Google into json, but that is basically what I want to do. The only Rails related documentation I found in Google API documentation was dated 2009, but it mentioned the gdata gem. Should I be using that?
Thank you so much for any help on this.
EDIT
This is the response I get from Google Oauth2 Playground on a 200 ok (https://developers.google.com/oauthplayground/):
Content-type: application/atom+xml; charset=UTF-8
-content-encoding: gzip
<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom'
xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'
xmlns:gContact='http://schemas.google.com/contact/2008'
xmlns:batch='http://schemas.google.com/gdata/batch' xmlns:gd='http://schemas.google.com/g/2005'>
<id>kaustubh.bhardwaj86#gmail.com</id><updated>2014-01-13T18:34:22.842Z</updated><category
scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/contact/2008#contact'/>
<title type='text'>Kaustubh Bhardwaj's Contacts</title>
EDIT #2
Ok, I've isolated my problem. I'm getting Net::HTTPUnauthorized on my get request. Here is the updated code for the get request I'm making to Google Contacts API:
uri = URI.parse("https://www.google.com/m8/feeds/contacts/default/full?max-results=50000")
# headers = { "access_token" => "#{auth_token}" }
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(uri.request_uri)
request.initialize_http_header({ "access_token" => "#{auth_token}" })
response = http.request(request)
puts response.code
puts response
That is the same url I am using in Google Oauth2 playground.
I suspect you're setting the token incorrectly. It is more normal to set it in an http header
Authorization: Bearer 0b79bab50daca910b000d4f1a2b675d604257e42
If you set it in the URL, I believe it's access_token=, rather than your oauth_token= but since I never use it this way, I might be wrong.

Parsing HTTPS instead of HTTP in Rails

I know how to parse HTTP requrests using Net::HTTP. How do you parse HTTPS requests?
I've tried and it says as a response (one of the lines):
<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>
stox
Edited:
So to show you in a little bit more detail:
For example, here is the HTTP API
https://xxxxx.info/merchant/balance?password=$main_password
How do I use Net:http to run this? Or do I use something else?
Thanks
Stox
would be helpful if you showed us some code?
try setting these attributes
# assuming http is generated like this http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
# might need this as well?
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
You can achieve that by setting use_ssl to true before executing the request.
uri = URI(https://xxxxx.info/merchant/balance?password=$main_password)
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(req)
end
puts res.body
reference https://ruby-doc.org/stdlib-2.4.1/libdoc/net/http/rdoc/Net/HTTP.html#class-Net::HTTP-label-HTTPS

How can I Authenticate a http web request with a certificate?

I need to be able to send a http get request to a web service that requires clients to authenticate with a specific certificate. The .net code looks like this:
if (certificate != null)
request.ClientCertificates.Add(certificate);
return request;
I havent been able to figure out the equivalent in rails. Any suggestions?
If using basic net/https, then it's quite simple:
require 'net/https'
require 'uri'
uri = URI.parse(ARGV[0] || 'https://localhost/')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true if uri.scheme == "https" # enable SSL/TLS
http.key = pkey #Sets an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
http.cert= cert #Sets an OpenSSL::X509::Certificate object as client certificate
http.start {
http.request_get(uri.path) {|res|
print res.body
}
}
If you have them combined, you'll have to massage them using some openssl utility methods.
For a custom http client you should read the docs for the ruby openssl library for the gory details.
But in a nutshell, something like this should work:
ctx = OpenSSL::SSL::SSLContext.new
ctx.key = private_key_file
ctx.cert = certificate_file
..and then supply the context to your connection.

Ruby on Rails HTTPS Post Bad Request

Greetings all.
My application works with a remote server. Server uses https
authorization of the certificate. I have following code to authorize and
sends request:
uri = URI.parse("https://db1-test.content.ertelecom.ru/")
http = Net::HTTP.new(uri.host, '443')
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.ca_file = File.join(File.dirname("public/certificate.pem"),
"certificate.pem")
http.start do |http|
req =
Net::HTTP::Get.new("/cgi-bin/expr/export.get_pay_systems?partner_id=1003")
responce = http.request(req)
resp = responce.body
end
this code works well, I get the data from the server. BUT when I try to
make POST request:
http.start do |http|
req =
Net::HTTP::Post.new("/cgi-bin/expr/payment_transactions.verify_order",
params)
responce = http.request(req)
resp = responce.body
end
I get an error from the server:
Your browser sent a request that this server could not understand.
Request header field is missing ':' separator.
what is that be? I tried to find a solution, but to no avail. the
Internet caught the message that it could be antivirus, but I'm on
Linux. I will be glad to any thoughts!
You're not filling the header data.
You could either use the Net::HTTP.post_form method to create your request or populate the form_data yourself.
post_form solution:
req = NET::HTTP.post_form("/cgi-bin/expr/payment_transactions.verify_order", params)
manual form_data population
req =
Net::HTTP::Post.new("/cgi-bin/expr/payment_transactions.verify_order")
req.set_form_data(params)

Resources