Ruby implementing oauth2 client credentials flow - ruby

I'm new to Ruby and I'm trying to implement a oauth2 with client credentials flow.
I've found the "ouath2" gem, but that requires a redirect_uri that I don't have.
Here is the gem.
Here is what I'm trying to implement
secret_id = 'this-is-a-secret-id'
token_id = 'this-is-a-token-id'
scope = 'such-a-good-scope'
grant_type = 'client_credentials'
#client = nil
# Get access token
def GetAccessToken
HttpRequest request = HttpRequest::Post("https://awesome-page.com/oauth/token")
request.content = {
{ "client_id" => token_id },
{ "client_secret" => secret_id }
{ 'grant_type' => grant_type },
{ 'scope' => scope}
}
response = request.send
json = response.content
accessToken = JsonConvert.DeserializeObject<Token>(json)
#client = Client.new(bearer: accessToken)
end
# Refresh token
def RefreshToken
HttpRequest request = HttpRequest::Post("https://awesome-page.com/oauth/token")
request.content = {
{ "client_id" => token_id },
{ "client_secret" => secret_id }
{ 'grant_type' => grant_type },
{ 'refresh_token' => scope}
}
response = request.send
json = response.content
accessToken = JsonConvert.DeserializeObject<Token>(json)
#client = Client.new(bearer: accessToken)
end
# End then implementing the "getting the resources with the client" part and so on...
Any idea how to do this, I'm getting a little bit desperate now
Any help is greatly appreciated!

You’re kind of going about this all wrong. With the Oauth2 gem you need to:
Initialise a new Oauth2::Client with your client ID, secret, scope, and define the token and redirect url (this is a url in your app that users logging in get sent back to. Not used for Client Credentials flow as it’s for server to server comms)
Call token = client.client_credentials.get_token. This sets token to an AccessToken it obtained.
Then call token.get(‘https://your-url.com/path/to/resource’) - or post/patch/delete.
Look at the access_token.rb file in the repo to see the methods you can call. They also take a series of params, for things like additional headers or body payload you can pass. It’s based on Faraday so you can always look up Faraday docs for help with that part.

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

how to handle token authentication in Angular Dart?

First time dealing with a SPA. I have a back-end restful service that returns a token when a user signs in. I know I am supposed to send the token through the headers in each request so I was thinking in saving the token in a file and create a service or a class that loads the token in every component but I don't know if this is a good approach as I can't find documentation for Angular Dart about this.
I saved the Token first in localStorage as Tobe O suggested:
Future login(username, password) async {
String url = 'http://127.0.0.1:8000/auth/login/';
var response =
await _client.post(url, body: {'username': username, 'password': password});
Map mapped_response = _decoder.convert(response.body);
window.localStorage.addAll({"token": mapped_response["key"]});
}
But still I was receiving 401 responses when I tried to get user information, this was the function:
Future check_authentification () async {
String _headers_key = "Authorization";
String _headers_value = "Token "+window.localStorage["token"];
var response = await _client.get("http://127.0.0.1:8000/auth/user/", headers: {_headers_key: _headers_value});
user_data = _decoder.convert(response.body);
response_status = response.statusCode;
}
I couldn't get authorized because django-rest-auth wasn't properly configured for token authorization. The solution was to add TokenAuthentication to the default authentication classes in django settings.
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
)}

Spotify authorization_code

I'm trying to allow my app to log into Spotify via their Authorization Code Flow. I am able to receive an authorization code from Spotify in the initial authorization step, but receive the following error when trying to get an access token:
{"error":"invalid_client","error_description":"Invalid client"}
My code is as follows:
# Callback from Spotify Authorization
get '/auth/spotify/callback' do
session[:code] = params[:code]
redirect to '/refresh'
end
Then, I am POSTing the following:
get '/refresh' do
uri = URI('https://accounts.spotify.com/api/token')
resp = Net::HTTP.post(uri,
{
"grant_type" => "authorization_code",
"code" => session[:code].to_s,
"redirect_uri" => "http://localhost:4567/auth/spotify/callback",
"client_id" => client_id,
"client_secret" => client_secret
}.to_json
)
"#{resp.body}"
end
Any help would be appreciated
EDIT: I've also tried to POST the same parameters above using PostMan, but receive the same error message
You need to add the Authorization header in your POST request.
Add the following key in your Net::HTTP.post options :
{'Authorization' => 'Basic YOUR_AUTH_CODE' }
EDIT :
This is in the docs under the 'Your application requests refresh and access tokens' heading.
To answer my own question:
I didn't need to make the request since the gem I was using, 'omniauth-spotify', could return the access token to me in request.env['omniauth.auth'].credentials.token
I was also creating the POST request incorrectly. The example below is the correct way to make a POST and obtain a new token from a refresh_token (provided in the above .credentials hash)
# Get new access token from refresh token
# session[:creds] = request.env['omniauth.auth'].credentials
get '/refresh' do
refresh_token = session[:creds].refresh_token
auth = "Basic " + Base64.strict_encode64("#{client_id}:#{client_secret}")
uri = URI.parse('https://accounts.spotify.com/api/token')
request = Net::HTTP::Post.new(uri)
request["Authorization"] = auth
request.set_form_data(
"grant_type" => "refresh_token",
"refresh_token" => refresh_token,
)
req_options = {
use_ssl: uri.scheme == "https",
}
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
http.request(request)
end
"#{response.code}" # > 200 OK
end

OAuth from script

I've seen a few questions/guides about implementing 3 step OAuth inside of a Rails app, but I'm interested in only the client side part of it. I'm trying to authenticate as a consumer to an OAuth2 server. As far as I can tell, I'm building the access token correctly.
#consumer = OAuth::Consumer.new(#config[:client_id], #config[:client_secret],
{
:site => "http://api.server.com",
:oauth_version => "2.0",
:http_method => :post,
:authorize_path => "/oauth2/authorize",
:access_token_path => "/oauth2/token",
:request_token_path => "/oauth2/request_token"
})
token_hash = { oauth_token: #config[:client_id], oauth_token_secret: #config[:client_secret] }
#access_token = OAuth::AccessToken.from_hash(#consumer, token_hash)
All of this succeeeds, the access token ends up looking like this:
#<OAuth::AccessToken:0x007fb6f2290468
#token="client_id",
#secret="secret",
#consumer=#<OAuth::Consumer:0x007fb6f2290800
#key="client_id",
#secret="secret",
#options={:signature_method=>"HMAC-SHA1",
:request_token_path=>"/outh2/request_token",
:authorize_path=>"/oauth2/authorize",
:access_token_path=>"/oauth2/token",
:proxy=>nil,
:scheme=>:header,
:http_method=>:post,
:oauth_version=>"2.0",
:site=>"https://api.server.com"
}
>,
#params={:oauth_token=>"client_id",
:oauth_token_secret=>"secret"
}>
The problem is that any request I make using #access_token returns a Forbidden error.
Any ideas what part of my request is wrong?

how to GET and POST on Twitter in ruby on rails using access tokens

i am using omniauth to authenticate a user via twitter. omniauth provides access tokens. now i want to send the get or post request to twitter. i dont want to use any gems. i want to do with net::http.
even in twitter api documentation ! I am not able to find a good tutorial for this
can any one help? thanks
Here it is exactly what you need, so, since you've got the token and the secret from omniauth, now you are going to use it:
def prepare_access_token(oauth_token, oauth_token_secret)
consumer = OAuth::Consumer.new("APIKey", "APISecret", { :site => "https://api.twitter.com", :request_token_path => '/oauth/request_token', :access_token_path => '/oauth/access_token', :authorize_path => '/oauth/authorize', :scheme => :header })
token_hash = { :oauth_token => oauth_token, :oauth_token_secret => oauth_token_secret }
access_token = OAuth::AccessToken.from_hash(consumer, token_hash )
access_token
end
Then you, for example, post a tweet:
msg = {'status' => 'Hey look I can tweet via OAuth!'}
access_token = prepare_access_token(token, secret)
response = access_token.post('https://api.twitter.com/1/statuses/update.json', msg, { 'Accept' => 'application/xml' })
Read the article presented on the link for more informations.

Resources