I'm trying to get an Oauth request token from Twitter - I'm following their guide (links below) and I've gone over every single step about 10 times but I cannot figure out why I'm getting this error:
{\"errors\":[{\"code\":32,\"message\":\"Could not authenticate you.\"}]}
They use Oauth 1.0. I'm supposed to combine all the percent-encoded params in my request, percent encode them, add the request method (POST) and the request url, use that plus my api key secret to create a signature, and add the signature to my final Post request. The signature docs are on this page and the subsequent Post request docs are on this page. I used the example code given in the guide to generate a signature and I got the same result the guide did, so I don't think the actual signature generation is a problem.
I triple checked my api key and api key secret and callback url.
Here's my full code. Can anyone see a problem?
require 'net/http'
require 'uri'
require 'json'
require 'base64'
require 'cgi'
require 'openssl'
oauth_callback = "http://www.example.localhost:3000/twittercallback"
oauth_consumer_key = '[KEY]'
oauth_timestamp = Time.now.to_i
oauth_nonce = SecureRandom.hex(10) + oauth_timestamp.to_s
oauth_signature_method = "HMAC-SHA1"
oauth_version = "1.0"
one1 = CGI.escape "oauth_callback"
one2 = CGI.escape oauth_callback
two1 = CGI.escape "oauth_consumer_key"
two2 = CGI.escape oauth_consumer_key
three1 = CGI.escape "oauth_nonce"
three2 = CGI.escape oauth_nonce
four1 = CGI.escape "oauth_signature_method"
four2 = CGI.escape oauth_signature_method
five1 = CGI.escape "oauth_timestamp"
five2 = CGI.escape oauth_timestamp.to_s
six1 = CGI.escape "oauth_version"
six2 = CGI.escape oauth_version
string = "#{one1}=#{one2}&#{two1}=#{two2}&#{three1}=#{three2}&#{four1}=#{four2}&#{five1}=#{five2}&#{six1}=#{six2}"
encoded_string = CGI.escape string
url = "https://api.twitter.com/oauth/request_token"
encoded_url = CGI.escape url
encoded_string = "POST&" + encoded_url + "&" + encoded_string
signing_key = '[SECRET]'
encoded_signing_key = (CGI.escape signing_key) + "&"
digest = OpenSSL::Digest::Digest.new( 'sha1' )
hmac = OpenSSL::HMAC.digest( digest, encoded_signing_key, encoded_string)
signature = Base64.encode64( hmac ).chomp.gsub( /\n/, '' )
uri = URI.parse("https://api.twitter.com/oauth/request_token")
request = Net::HTTP::Post.new(uri)
request.content_type = "application/x-www-form-urlencoded"
oauth = 'OAuth oauth_callback="' + (CGI.escape oauth_callback) + '", oauth_nonce="' + (oauth_nonce + 'dsd') + '", oauth_signature_method="HMAC-SHA1", oauth_timestamp="' + oauth_timestamp.to_s + '", oauth_consumer_key="`<KEY>", oauth_signature="' + (CGI.escape signature) + '", oauth_version="1.0"'
request['Authorization'] = oauth
request.body = ""
req_options = {
use_ssl: uri.scheme == "https",
}
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
http.request(request)
end
here's my request["Authorization"]
header
"OAuth oauth_callback=\"http%3A%2F%2Fwww.example.localhost%3A3000%2Ftwittercallback\", oauth_nonce=\"e5686f142ca58a45af233fb66876671592457549dsd\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"1592457549\", oauth_consumer_key=\"[KEY]\", oauth_signature=\"n9MNLO%2Ft%2FT6WX0Myu5JcTICXNAQ%3D\", oauth_version=\"1.0\""
and this is the example header from Twitter's docs:
OAuth oauth_nonce="K7ny27JTpKVsTgdyLdDfmQQWVLERj2zAK5BslRsqyw", oauth_callback="http%3A%2F%2Fmyapp.com%3A3005%2Ftwitter%2Fprocess_callback", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1300228849", oauth_consumer_key="OqEqJeafRSF11jBMStrZz", oauth_signature="Pc%2BMLdv028fxCErFyi8KXFM%2BddU%3D", oauth_version="1.0"
You don't provide the required Authorization
header, but separate headers for each parameter. You need to combine the parameters in the following form as a string with OAuth
as first:
OAuth oauth_nonce="K7ny27JTpKVsTgdyLdDfmQQWVLERj2zAK5BslRsqyw",oauth_callback="http%3A%2F%2Fmyapp.com%3A3005%2Ftwitter%2Fprocess_callback",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1300228849",oauth_consumer_key="OqEqJeafRSF11jBMStrZz",oauth_signature="Pc%2BMLdv028fxCErFyi8KXFM%2BddU%3D",oauth_version="1.0"
And provide that in the Authorization
header:
request['Authorization'] = <OAuth string>
There are a lot of other issues in your code why it's not working:
dsd
to the nonce
in the Authorization
headerSo this results in the following if we rework your code:
require 'net/http'
require 'uri'
require 'base64'
require 'cgi'
require 'openssl'
require 'securerandom'
oauth_consumer_key = CGI::escape('D6O7BIWc6MTgl0A8UaRRt83In')
oauth_timestamp = CGI::escape(Time.now.to_i.to_s)
oauth_nonce = CGI::escape(SecureRandom.hex(10) + oauth_timestamp.to_s)
oauth_signature_method = CGI::escape("HMAC-SHA1")
oauth_version = CGI::escape("1.0")
one1 = CGI::escape("oauth_consumer_key")
one2 = oauth_consumer_key
two1 = CGI::escape("oauth_nonce")
two2 = oauth_nonce
three1 = CGI::escape("oauth_signature_method")
three2 = oauth_signature_method
four1 = CGI::escape("oauth_timestamp")
four2 = oauth_timestamp
five1 = CGI::escape("oauth_version")
five2 = oauth_version
string = "#{one1}=#{one2}&#{two1}=#{two2}&#{three1}=#{three2}&#{four1}=#{four2}&#{five1}=#{five2}"
encoded_string = CGI.escape string
url = "https://api.twitter.com/oauth/request_token"
encoded_url = CGI.escape url
encoded_string = "POST&" + encoded_url + "&" + encoded_string
signing_key = 'xJPBJ2OdV6nM8r9e6ZysbnHTrZYm4R7LaY9OafNNNY3BkT4Oym'
encoded_signing_key = signing_key + '&'
digest = OpenSSL::Digest.new( 'sha1' )
hmac = OpenSSL::HMAC.digest( digest, encoded_signing_key, encoded_string)
signature = Base64.encode64( hmac ).chomp.gsub( /\n/, '' )
uri = URI.parse("https://api.twitter.com/oauth/request_token")
request = Net::HTTP::Post.new(uri)
request.content_type = "application/x-www-form-urlencoded"
oauth = 'OAuth ' + one1 + '="' + oauth_consumer_key + '", ' + two1 + '="' + oauth_nonce + '", ' + CGI::escape('oauth_signature') + '="' + (CGI.escape signature) + '", ' + three1 + '="HMAC-SHA1", ' + four1 + '="' + oauth_timestamp.to_s + '", ' + five1 + '="1.0"'
puts oauth
request['Authorization'] = oauth
request.body = ""
req_options = {
use_ssl: uri.scheme == "https",
}
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
http.request(request)
end
puts response
puts response.body
I would also like to give you a way to handle the parameters in a better way to generate the base_string
and the header
:
require 'net/http'
require 'uri'
require 'base64'
require 'cgi'
require 'openssl'
require 'securerandom'
oauth_consumer_key = '<KEY>'
oauth_consumer_secret = '<SECRET>'
oauth_consumer_secret += '&'
oauth_timestamp = Time.now.getutc.to_i.to_s
oauth_nonce = SecureRandom.hex(10) + oauth_timestamp
oauth_signature_method = "HMAC-SHA1"
oauth_version = "1.0"
url = "https://api.twitter.com/oauth/request_token"
uri = URI.parse(url)
params = {
'oauth_consumer_key' => oauth_consumer_key,
'oauth_nonce' => oauth_nonce,
'oauth_signature_method' => oauth_signature_method,
'oauth_timestamp' => oauth_timestamp.to_s,
'oauth_version' => oauth_version
}
base_string = "POST&" + CGI::escape(url) + "&" + CGI::escape(params.sort.collect{ |k,v| "#{CGI::escape(k)}=#{CGI::escape(v)}" }.join('&'))
oauth_signature = Base64.encode64("#{OpenSSL::HMAC.digest('sha1', oauth_consumer_secret, base_string)}").chomp
params['oauth_signature'] = oauth_signature
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
header = "OAuth " + params.sort.collect {|k,v| "#{CGI::escape(k)}=\"#{CGI::escape(v)}\""}.join(", ")
rsp, data = http.post(uri, nil, {'Authorization' => header })
puts rsp
puts rsp.body
Another suggestion is to use the existing OAuth
gem available. When you use this well known library you are also able to get support from Twitter
.
gem install oauth
require 'oauth'
oauth_consumer = OAuth::Consumer.new("<KEY>", "<SECRET>", :site => "https://api.twitter.com")
access_token = OAuth::AccessToken.new(oauth_consumer)
request_token = access_token.request(:post, "/oauth/request_token")
rsp = request_token.body
puts rsp
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With