Ruby : Twitter API V2.0 error 401 Unauthorized (OAuth) - ruby

I'm currently creating a Twitter bot in Ruby. This bot must tweet the result of a web scraping everyday. Everything works perfectly but I have a problem with the API part. I got all of my API keys from Twitter and I can tweet from Postman. I got the request directly from Postman but for some reason, when I try to execute my script in my terminal, I got this message :
{
"title": "Unauthorized",
"type": "about:blank",
"status": 401,
"detail": "Unauthorized"
}
I'm currently testing my script from my terminal but it will be executed from heroku with the scheduler in the future.
Do you understand where the problem comes from? I read that I might need to create callbacks URL in Twitter Dashboard but I have no idea what I need to enter as I'm testing my script in the terminal.
Thanks :)
Here's my script (I skipped the scraping part):
require "json"
require "net/http"
require "open-uri"
require "nokogiri"
#Tweeting result
url = URI("https://api.twitter.com/2/tweets")
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = "OAuth oauth_consumer_key='#{ENV['CONSUMER_KEY']}',oauth_token='#{ENV['ACCESS_TOKEN']}',oauth_signature_method='HMAC-SHA1',oauth_timestamp='1644297052',oauth_nonce='yxABTHBJyWM',oauth_version='1.0',oauth_signature='pWxeExVF46ueXyblWfz4vzzuRCg%3D'"
request["Content-Type"] = "application/json"
request["Cookie"] = "guest_id=v1%3A164429705219615684"
request.body = JSON.dump({
"text": "Nombre d'adhérents : #{total}"
})
response = https.request(request)
puts response.read_body

Related

OAuth error in using twitter API v2 for posting a tweet

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

Google Signin from server side app in ruby

I have a mobile app that is signin in with google and sending a server auth code to my backend app.
I want to use this code, along with the client secrets from the google developer console, to retrieve a refresh code for retrieving data from google drive when the user is offline.
Google provides an client for auth calls in ruby, but it seems not to be maintained lately and I could not see a way to do this kind of authorisation in the docs.
In the documentation, I could find an example of how to do this on python:
from oauth2client import client
# Exchange auth code for access token, refresh token, and ID token
credentials = client.credentials_from_clientsecrets_and_code(
CLIENT_SECRET_FILE,
['https://www.googleapis.com/auth/drive.appdata', 'profile', 'email'],
auth_code)
I would like to do this in ruby through a post to their https://www.googleapis.com/oauth2/v4/token endpoint. Here is what I've tried so far:
require 'uri'
require 'net/http'
require 'json'
url = URI("https://www.googleapis.com/oauth2/v4/token")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
headers = {'Content-Type': 'application/json'}
request = Net::HTTP::Post.new(url.request_uri, headers)
request.body = {
code: "#{server_auth_code_sent_to_api}",
client_id: "#{client_id_from_developer_console}",
client_secret: "#{client_secret_from_developer_console}",
grant_type: 'authorization_code',
redirect_url: '',
}.to_json
response = http.request(request)
puts JSON.parse(response.read_body)
But I keep getting the following error:
{
"error": "unsupported_grant_type",
"error_description": "Invalid grant_type: "
}
Does anybody has an idea on what I'm doing wrong, or has a working example on how to do this kind of authorisation?
Thanks in advance.
In case somebody stumbles here with a similar problem, what caused the request to fail was the Content-Type, and not the grant_type parameter.
Digging around in the code for the client library I saw that they use application/x-www-form-urlencoded the endpoint expects a application/x-www-form-urlencoded content type. I adjusted my code accordingly and was able to get a successful response with the valid credentials and token.
Here follows the resulting code:
require 'uri'
require 'net/http'
require 'json'
url = URI("https://www.googleapis.com/oauth2/v4/token")
params = {
"code" => "#{server_auth_code_sent_to_api}",
"client_id" => "#{client_id_from_developer_console}",
"client_secret" => "#{client_secret_from_developer_console}",
"grant_type" => "authorization_code",
"redirect_url" => "#{redirect_url_from_developer_console}",
}
response = Net::HTTP.post_form(url, params)
puts JSON.parse(response.read_body)

Post Request from client With Proper Headers Always return 401- UnAuthorized

Am a newbie to ruby. I am trying to invoke a REST API from ruby code. I have tried the code with both as standalone ruby code and inside a application using sinatra. But am facing same issue every where. Below are the options I have tried.
My Code to Invoke Post API using RestClient. Have modified the data to make it generic
url="https://myapiurl.com/endpointname"
person={"name"=>"Sample Name","age"=>"20"}
headers={
"Content-Type"=>"application/json",
"api-key"=>"ABCDEFGHIJKL"
}
response=RestClient.post url,person.to_json,headers
puts response
I wrote the above block of code in a function and tried calling. But I got response as below
{"status": 401, "error":"Unauthorized Access"}
I tried the same api via postman with below settings and was able to get a proper response from the api.
URL : https://myapiurl.com/endpointname
Headers : Content-Type: application/json, api-key:"ABCDEFGHIJKL"
Body: Raw:application/json: {"name":"Sample Name","age":"20"}
When I tried not passing api-key in the headers via postman I got similar response as I got via ruby code.
Next I tried generating code from postman option. Below is the code that got generated from postman for Ruby(Net::Http).
I removed the post params of cache-control and postman-token from the ruby code and tried running the ruby code. Again I got the same 'Unauthorized' response only !
require 'uri'
require 'net/http'
url = URI("https://myapiurl.com/endpointname")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(url)
request["content-type"] = 'application/json'
request["api-key"] = 'ABCDEFGHIJKL'
request["cache-control"] = 'no-cache'
request["postman-token"] = 'some value got generated'
request.body = "{\"name\":\"Sample Name\",\"age\":\"20\"}"
response = http.request(request)
puts response.read_body
I suspected is it some IP level issue is blocking hence tried used curl command to hit the api as below. I got proper response from the API.
curl -X POST \
https://myapiurl.com/endpointname \
-H 'content-type: application/json' \
-H 'api-key: ABCDEGHIJKL' \
-d '{"name":"Sample Name","age":"20"}'
I believe that the api-key is not getting passed to the request via the ruby code. Is my way of sending the api-key in headers via ruby code correct?

opt_fields not working with Asana API but url works in the Asana explorer

I'm using Ruby starting from the "hello world" example. Hello world works find. I'm trying to get this GET to work:
GET /projects/192372431230306/tasks?opt_fields=id,assignee,due_on,name,notes&limit=10&completed_since=now
It works exactly as expected in the Asana API explorer.
I'm using the same URI in my code:
uri = URI.parse("https://app.asana.com/api/1.0/projects/192372431230306/tasks?opt_fields=id,assignee,due_on,name,notes&limit=10&completed_since=now")
It still returns ID and Name correctly, but it's not what I want.
I can't see why it works in the explorer but not in the GET request. I am using the personal token and the explorer uses OAuth.
My personal token is set correctly before this code. I can create tasks, get projects, get tasks. I just can't more fields in this query like the API explorer.
Added Code:
uri = URI.parse("https://app.asana.com/api/1.0/projects/192372431230306/tasks?opt_fields=id,assignee,due_on,name,notes&limit=10&completed_since=now")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
# set up the request
header = {
"Content-Type" => "application/json"
}
req = Net::HTTP::Get.new(uri.path, header)
req.basic_auth(personal_access_token, '')
res = http.start { |http| http.request(req) }
body = JSON.parse(res.body)
puts "projects: #{body['data']}"
There isn't quite enough code here to know what's wrong. It sounds like your code is working to some degree, but maybe there's something different between using a personal token as opposed to OAuth. According to the Asana API documentation:
Personal Access Tokens should be used similarly to OAuth access tokens when accessing the API, passing them in the Authorization header:
curl -H "Authorization: Bearer ACCESS_TOKEN" https://app.asana.com/api/1.0/users/me
So in addition to the URL, you need to use the correct headers. I'm not sure what HTTP library you use, but here's how I would do it with HTTParty:
require 'uri'
require 'httparty'
uri = URI.parse("https://app.asana.com/api/1.0/projects/192372431230306/tasks?opt_fields=id,assignee,due_on,name,notes&limit=10&completed_since=now")
task = HTTParty.get(uri,
:headers =>
{'Authorization' => "Bearer ${ENV[ACCESS_TOKEN]}"}
).parsed_response
If you put your personal access token in the ACCESS_TOKEN environment variable, that should correctly authenticate you for the request.
Still, it seems like there's something else missing if you can get all the fields. Maybe your personal access token is associated with a different account than your OAuth access token? It might help to check the results of /users/me/ route?

403 Error response from single URI request?

When trying the live API explorer and using my api key I can get a good response. However when using the same URI link generated from the explorer within my application, I get a 403 error. However, the application makes only 1 request.
url = URI.parse("http://api.espn.com/v1/sports/basketball/nba/athletes/355?
apikey=xxxxxxxxxxxxxxxx")
req = Net::HTTP::Get.new(url.path)
res = Net::HTTP.start(url.host, url.port) {|http|
http.request(req)
}
puts res.body
Here's the response in the terminal:
{
"status": "error",
"code": 403,
"message" : "Account Inactive"
}
403 should mean I'm exceeding my limit, but the message says Account Inactive. This is a new account and I got confirmation that it was active. Plus my API key works in the web interface.
Any ideas?

Resources