I'm trying to call the AWS API gateway resource /apikeys to get a list of my api keys. This isn't a part of the sdk yet so I'm trying to do the web service call in Ruby and I'm getting a 400. What I've read indicates I'm not setting up the headers and body correctly. Any help would be appreciated. I'm using the aws4 gem for signing the headers and the faraday gem to make the GET call. Here's the code & output.
#! /home/sburke/.rvm/rubies/ruby-2.2.3/bin/ruby
require 'aws4'
require 'pp'
require 'faraday'
require 'json'
require 'time'
# create a signer
signer = AWS4::Signer.new(
access_key: ENV['AWS_ACCESS_KEY_ID'],
secret_key: ENV['AWS_SECRET_ACCESS_KEY'],
region: "us-east-1"
)
# build request
headers = {
"Date" => Time.now.httpdate,
"Content-Type" => "application/x-amz-json-1.0",
"host" => "https://apigateway.us-east-1.amazonaws.com"
}
body="{}"
# sign headers
uri = URI("https://apigateway.us-east-1.amazonaws.com/apikeys")
headers_signed = signer.sign("GET", uri, headers, body)
pp headers_signed
conn = Faraday.new(:url => "https://apigateway.us-east-1.amazonaws.com")
resp = conn.get do |req|
req.url '/apikeys'
req.headers = headers_signed
end
pp resp.status
Here's the output of the code.
sburke#sburke-laptop:~/sandbox/aws_rest_ruby$ ./test.rb
Digest::Digest is deprecated; use Digest
Digest::Digest is deprecated; use Digest
Digest::Digest is deprecated; use Digest
Digest::Digest is deprecated; use Digest
Digest::Digest is deprecated; use Digest
{"Date"=>"Sun, 11 Oct 2015 02:14:13 GMT",
"Content-Type"=>"application/x-amz-json-1.0",
"host"=>"https://apigateway.us-east-1.amazonaws.com",
"Authorization"=>
"AWS4-HMAC-SHA256 Credential=AKIAI2P7SWBU6FBHPMOA/20151011/us-east-1/apigateway/aws4_request, SignedHeaders=content-type;date;host, Signature=85536b2084c11492d7b1af612b4f0a83752bb921e8534155dfbebeaefde6a73d"}
400
Here's the output of the headers and the body is empty. I don't get any more info from the faraday response object aside from the 400 and headers.
pp resp.headers
pp resp.body
{"transfer-encoding"=>"chunked",
"date"=>"Wed, 14 Oct 2015 17:27:25 GMT",
"connection"=>"close"}
""
Related
I want to consume some API data from a Rails app. A curl example is curl --data 'api_key=your_api_key&api_secret=your_api_secret&host_id=your_user_host_id' https://api.zoom.us/v1/webinar/list I have experimented with this at the terminal and I am seeing expected responses. I’m now experimenting in a ruby script using httparty. My question is how should I handle the ‘stuff’ before the endpoint (api_key…secret…ect)? Are these headers?
In regard to curl --data only tells me that it is a post request, but I'm not sure how that translates to httparty.
Here is a first attempt:
require 'httparty'
api_key = 'myKey'
api_secret = 'secret'
host_id = 'host'
data_type = 'JSON'
response = HTTParty.post("api_key&api_secret&host_id&data_type https://api.zoom.us/v1/webinar/list/registration")
puts response.parsed_response
But this gives me a bad URI response. If I run this same script with the endpoint only I do get a response code back from zoom saying that API key and secret are required.
Looking at this example I think this should work:
require 'httparty'
api_key = 'myKey'
api_secret = 'secret'
host_id = 'host'
data_type = 'JSON'
options = {
body: {
api_key: api_key,
api_secret: api_secret,
host_id: host_id,
data_type: data_type
}
}
response = HTTParty.post("https://api.zoom.us/v1/webinar/list/registration", options)
puts response.parsed_response
I get the response:
{"error"=>{"code"=>200, "message"=>"Invalid api key or secret."}}
which I think is a step in the right direction.
No those are not headers those are parameters. Header are usually denoated by the -H flag.
Try this:
require 'httparty'
query_params = {api_key: 'myKey',
api_secret: 'secret',
host_id: 'host',
data_type: 'JSON'}
response = HTTParty.post("api_key&api_secret&host_id&data_type https://api.zoom.us/v1/webinar/list/registration", :query => query_params)
puts response.parsed_response
I am having a very hard time getting access to the quickblox API. Based on their documentation this code should work:
require 'base64'
require 'cgi'
require 'openssl'
require 'hmac-sha1'
# Application credentials
aPPLICATION_ID = 12345
aUTH_KEY = 'hidden'
aUTH_SECRET = 'hidden'
# Generate signature
timestamp = Time.now.in_time_zone('UTC').to_i
nonce = timestamp-425346
signature_string = "application_id=#{aPPLICATION_ID}&auth_key=#{aUTH_KEY}&nonce=#{nonce}×tamp=#{timestamp}"
signature =Base64.encode64("#{ OpenSSL::HMAC.digest('sha1', signature_string, aUTH_SECRET) }")
# Post
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
request = Net::HTTP::Post.new("/session.json")
request.add_field('QuickBlox-REST-API-Version', '0.1.1')
request.add_field('Content-Type', 'application/json')
request.add_field('Accept', '*/*')
request.body = {"application_id" => aPPLICATION_ID, "auth_key" => aUTH_KEY, "nonce" => nonce, "timestamp" => timestamp, "signature" => signature }.to_json
response = http.request(request)
However I keep getting an error: {"errors":{"base":["Unexpected signature"]}}
Even when using their hurl: http://hurl.quickblox.com/ i get the exact same error. Very frustrating. What exactly am I doing wrong?
As it turns out, it looks like QuickBlox does not accept the signature has produced by ruby for some reason. QuickBlox even removed their own ruby gem (in which also generates this same error).
Right now the solution is to grab the token via a php script first then do your calls in ruby.
Check my stackoverflow answer on this issue here.
This mostly boils down to these two lines:
signature_string = "application_id=#{aPPLICATION_ID}&auth_key=#{aUTH_KEY}&nonce=#{nonce}×tamp=#{timestamp}"
signature =Base64.encode64("#{ OpenSSL::HMAC.digest('sha1', signature_string, aUTH_SECRET) }")
You can check this quickblox_api gem too. It worked greatly for me.
https://developers.google.com/gdata/articles/using_ruby
I'm following the "Authentication | Using the Google Spreadsheets API" section in above tutorial.
rb(main):008:0> require 'net/https'
=> true
irb(main):009:0> http = Net::HTTP.new('www.google.com', 443)
=> #<Net::HTTP www.google.com:443 open=false>
irb(main):010:0> http.use_ssl = true
=> true
irb(main):011:0> path = '/accounts/ClientLogin'
=> "/accounts/ClientLogin"
# Now we are passing in our actual authentication data.
# Please visit OAuth For Installed Apps for more information
# about the accountType parameter
irb(main):014:0> data = \
irb(main):015:0* 'accountType=HOSTED_OR_GOOGLE&Email=your email' \
irb(main):016:0* '&Passwd=your password' \
irb(main):017:0* '&service=wise'
irb(main):018:0> headers = \
irb(main):019:0* { 'Content-Type' => 'application/x-www-form-urlencoded'}
=> {"Content-Type"=>"
application/x-www-form-urlencoded"}
# Post the request and print out the response to retrieve our authentication token
irb(main):020:0> resp, data = http.post(path, data, headers)
=> #<Net::HTTPLengthRequired 411 Length Required readbody=true>
I'm supposed to get "=> [#, "SID=DQAAAIIAAADgV7j4F-QVQjnxdDRjpslHKC3M ... [ snipping out the rest of the authentication strings ]" after the POST. I put my gmail id and password in email and password.
What is problem?
It's possible that they removed the service today. When I request this page https://www.google.com/accounts/ClientLogin I receive 404 (not found).
I am trying to setup the Spotify IOS API but everytime I run this Ruby file and go to http://localhost:1234/swap I get "Sinatra doesn't know this ditty".
Here is my code:
require 'sinatra'
require 'net/http'
require 'net/https'
require 'base64'
require 'json'
require 'encrypted_strings'
# This is an example token swap service written
# as a Ruby/Sinatra service. This is required by
# the iOS SDK to authenticate a user.
#
# The service requires the Sinatra and
# encrypted_strings gems be installed:
#
# $ gem install sinatra encrypted_strings
#
# To run the service, enter your client ID, client
# secret and client callback URL below and run the
# project.
#
# $ ruby spotify_token_swap.rb
#
# IMPORTANT: The example credentials will work for the
# example apps, you should use your own in your real
# environment. as these might change at any time.
#
# Once the service is running, pass the public URI to
# it (such as http://localhost:1234/swap if you run it
# with default settings on your local machine) to the
# token swap method in the iOS SDK:
#
# NSURL *swapServiceURL = [NSURL urlWithString:#"http://localhost:1234/swap"];
#
# -[SPAuth handleAuthCallbackWithTriggeredAuthURL:url
# tokenSwapServiceEndpointAtURL:swapServiceURL
# callback:callback];
#
print "\e[31m------------------------------------------------------\e[0m\n"
print "\e[31mYou're using example credentials, please replace these\e[0m\n"
print "\e[31mwith your own and remove this silly warning.\e[0m\n"
print "\e[31m------------------------------------------------------\e[0m\n"
print "\7\7"
sleep(2)
CLIENT_ID = ""
CLIENT_SECRET = ""
ENCRYPTION_SECRET = ""
CLIENT_CALLBACK_URL = "dawgone://returnhere"
AUTH_HEADER = "Basic " + Base64.strict_encode64(CLIENT_ID + ":" + CLIENT_SECRET)
SPOTIFY_ACCOUNTS_ENDPOINT = URI.parse("https://accounts.spotify.com")
set :port, 1234 # The port to bind to.
set :bind, '0.0.0.0' # IP address of the interface to listen on (all)
post '/swap' do
# This call takes a single POST parameter, "code", which
# it combines with your client ID, secret and callback
# URL to get an OAuth token from the Spotify Auth Service,
# which it will pass back to the caller in a JSON payload.
auth_code = params[:code]
http = Net::HTTP.new(SPOTIFY_ACCOUNTS_ENDPOINT.host, SPOTIFY_ACCOUNTS_ENDPOINT.port)
http.use_ssl = true
request = Net::HTTP::Post.new("/api/token")
request.add_field("Authorization", AUTH_HEADER)
request.form_data = {
"grant_type" => "authorization_code",
"redirect_uri" => CLIENT_CALLBACK_URL,
"code" => auth_code
}
response = http.request(request)
# encrypt the refresh token before forwarding to the client
if response.code.to_i == 200
token_data = JSON.parse(response.body)
refresh_token = token_data["refresh_token"]
encrypted_token = refresh_token.encrypt(:symmetric, :password => ENCRYPTION_SECRET)
token_data["refresh_token"] = encrypted_token
response.body = JSON.dump(token_data)
end
status response.code.to_i
return response.body
end
post '/refresh' do
# Request a new access token using the POST:ed refresh token
http = Net::HTTP.new(SPOTIFY_ACCOUNTS_ENDPOINT.host, SPOTIFY_ACCOUNTS_ENDPOINT.port)
http.use_ssl = true
request = Net::HTTP::Post.new("/api/token")
request.add_field("Authorization", AUTH_HEADER)
encrypted_token = params[:refresh_token]
refresh_token = encrypted_token.decrypt(:symmetric, :password => ENCRYPTION_SECRET)
request.form_data = {
"grant_type" => "refresh_token",
"refresh_token" => refresh_token
}
response = http.request(request)
status response.code.to_i
return response.body
end
This is because swap is a POST endpoint. When you pull up a URL in your browser you are doing an HTTP GET.
If you want to see that the sinatra service is running and you can at least talk to it you could try hitting it with curl from the command line with the right POST parameters.
Is there a way to check for an HTTPS status code in ruby? I know that there are ways to do this in HTTP using require 'net/http', but I'm looking for HTTPS. Maybe there is a different library that I need to use?
You can do this in net/http:
require "net/https"
require "uri"
uri = URI.parse("https://www.secure.com/")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri.request_uri)
res = http.request(request)
res.code #=> "200"
Refs:
Net::HTTP cheat sheet
How to Cure Net::HTTP’s Risky Default HTTPS Behavior
You can use any wrapper around Net::HTTP(S) to get much easier behavior.
I use Faraday here ( https://github.com/lostisland/faraday ) but HTTParty has almost the same functionality ( https://github.com/jnunemaker/httparty )
require 'faraday'
res = Faraday.get("https://www.example.com/")
res.status # => 200
res = Faraday.get("http://www.example.com/")
res.status # => 200
(as a bonus you get options for parsing responses, raising state exceptions, logging requests....
connection = Faraday.new("https://www.example.com/") do |conn|
# url-encode the body if given as a hash
conn.request :url_encoded
# add an authorization header
conn.request :oauth2, 'TOKEN'
# use JSON to convert the response into a hash
conn.response :json, :content_type => /\bjson$/
# ...
conn.adapter Faraday.default_adapter
end
connection.get("/")
# GET https://www.example.com/some/path?query=string
connection.get("/some/path", :query => "string")
# POST, PUT, DELETE, PATCH....
connection.post("/some/other/path", :these => "fields", :will => "be converted to a request string in the body"}
# add any number of headers. in this example "Accept-Language: en-US"
connection.get("/some/path", nil, :accept_language => "en-US")
require 'uri'
require 'net/http'
res = Net::HTTP.get_response(URI('http://www.example.com/index.html'))
puts res.code # -> '200'
Slightly more readable way:
response.kind_of?(Net::HTTPOK)