Newsletter2go ruby API client authorisation with get_token method - ruby-on-rails

Good day!
Can you please give me an example of authentication request with ruby newsletetr2go API client.
I can't figure it out.
I can connect to API using direct requests like RestClient.post "#{link}/oauth/v2/token", credentials, default_header
In credentials I use my username, password and grant_type, converted to json format
In default header I use content_type: 'application/json' and authorization: "Basic #{Base64.strict_encode64(ENV['NEWSLETTER2GO_AUTH_KEY'])}"
And it works fine. But when I try to use newsletter2go get_token method all I receive is "BAD REQUEST" error.
I'm using initializer to configure SwaggerClient like this:
SwaggerClient.configure do |config|
# Configure OAuth2 access token for authorization: OAuth
config.password = ENV['NEWSLETTER2GO_PASSWORD']
config.username = ENV['NEWSLETTER2GO_USERNAME']
config.api_key = ENV['NEWSLETTER2GO_AUTH_KEY']
end
After that I use newsletter2go api method call
SwaggerClient::AuthorizationApi.new.get_token("https://nl2go.com/jwt")
Seems everything is correct, but error "BAD REQUEST" happens all the time.
I followed the instructions, install swagger_client with ruby extentions in github, and newsletter2go methods are now available from my rails environment.
If I grab access_token manually and add it to my initializer, then do some requests like SwaggerClient::ListApi.new.get_lists it gives me a proper response with status 200 and list_ids
But SwaggerClient::AuthorizationApi.new.get_token("https://nl2go.com/jwt") does not work and this is the issue.
Any help would be very appreciated!

I figured out the reason why Newsletter2Go API ruby client does not grab api_key value. For some reason it's hardcoded to setup basic auth token which stands from username and password packed.
Here is code from
module SwaggerClient
class Configuration
# Gets Basic Auth token string
def basic_auth_token
'Basic ' + ["#{username}:#{password}"].pack('m').delete("\r\n")
end
# Returns Auth Settings hash for api client.
def auth_settings
{
'OAuth' =>
{
type: 'oauth2',
in: 'header',
key: 'Authorization',
value: "Bearer #{access_token}"
},
'Basic' =>
{
type: 'basic',
in: 'header',
key: 'Authorization',
value: basic_auth_token
},
}
end

Related

Getting authorization from iNaturalist for API

I'm trying to use iNaturalist's API via Ruby on Rails. I'm new to Ruby and iNaturalist's documentation is pretty sparse. As a first step, I need to figure out how to get authorization from their site.
iNaturalist provides the sample code below. I set up a project with iNaturalist and tried running the sample code in Rails Console with my credentials. #{url} in the following line is replaced with a url that the user is supposed to go to in order to log in to iNat:
puts "Go to #{url}, approve the app, and you should be redirected to your " +
"redirect_uri. Copy and paste the 'code' param here."
I went to the resulting url and logged in:
https://www.inaturalist.org/oauth/authorize?client_id=[my client id]&redirect_uri=https://ruby_on_rails--timrobinson41199691.codeanyapp.com/login/&response_type=code
iNaturalist responds with "The redirect uri included is not valid."
If I leave off &response_type=code, it responds with "The authorization server does not support this response type."
My website is on codeanywhere.com. The url of the main page is "https://ruby_on_rails--timrobinson41199691.codeanyapp.com/". Part of the problem is that I don't understand what kind of page I'm supposed to create for redirect_uri, since I'm still kind of new at this.
require 'rubygems'
require 'rest_client'
require 'json'
site = "https://www.inaturalist.org"
app_id = 'YOUR APP ID'
app_secret = 'YOUR APP SECRET'
redirect_uri = 'YOUR APP REDIRECT URI' # you can set this to some URL you control for testing
# REQUEST AN AUTHORIZATION CODE
# Your web app should redirect the user to this url. They should see a screen
# offering them the choice to authorize your app. If they aggree, they will be
# redirected to your redirect_uri with a "code" parameter
url = "#{site}/oauth/authorize?client_id=#{app_id}&redirect_uri=#{redirect_uri}&response_type=code"
# REQUEST AN AUTH TOKEN
# Once your app has that code parameter, you can exchange it for an access token:
puts "Go to #{url}, approve the app, and you should be redirected to your " +
"redirect_uri. Copy and paste the 'code' param here."
print "Code: "
auth_code = gets.strip
puts
payload = {
:client_id => app_id,
:client_secret => app_secret,
:code => auth_code,
:redirect_uri => redirect_uri,
:grant_type => "authorization_code"
}
puts "POST #{site}/oauth/token, payload: #{payload.inspect}"
puts response = RestClient.post("#{site}/oauth/token", payload)
puts
# response will be a chunk of JSON looking like
# {
# "access_token":"xxx",
# "token_type":"bearer",
# "expires_in":null,
# "refresh_token":null,
# "scope":"write"
# }
# Store the token (access_token) in your web app. You can now use it to make authorized
# requests on behalf of the user, like retrieving profile data:
token = JSON.parse(response)["access_token"]
headers = {"Authorization" => "Bearer #{token}"}
puts "GET /users/edit.json, headers: #{headers.inspect}"
puts RestClient.get("#{site}/users/edit.json", headers)
puts
After the user logs in to iNat, he should be redirected back to my website with the authorization code provided in the data. In routes.rb, my login route is set as:
post '/login', to: 'organisms#login'
I've tried using get, as well.
iNat is returned the error mentioned above and not redirecting back to my site.
OAuth can be a bit daunting at first. And that guide really just shows the equivalent of using cURL to test your API.
In an actual application redirect_uri is whatever endpoint in your application that handles the response when the provider redirects back from authorization.
So lets setup a minimal real rails app.
1. Register your app
Register a new application or edit your existing app.
Use http://localhost:3000/oauth/inaturalist/callback for the callback url (adjust the host as needed).
Keep the window open as you will need the client_id and secret in a moment.
2. Setup your routes
# /config/routes.rb
Rails.application.routes.draw do
# just make sure you have a root path defined.
root to: 'pages#home'
namespace :oauth do
namespace :inaturalist, controller: :callbacks do
# This is just a simple redirect route
get '/', action: :passthru, as: :authorize
# This is the route that handles the actual callback
get 'callback'
end
end
end
You can actually do this without the redirect route and just plant a link to the https://www.inaturalist.org/oauth/authorize... url in your view. But having it isolates your application against the craziness that is OAuth and its how OmniAuth does it.
3. Add your credentials to the Rails app.
In Rails 5 use the encrypted credentials to store your client_id and secret.
Run $ bin/rails credentials:edit from your shell.
inaturalist:
client_id: <from the inaturalist site>
secret: <from the inaturalist site>
In earlier versions use ENV vars instead.
4. Install the oauth2 gem
# Place this in your gemfile outside any groups
gem 'oauth2', '~> 1.4', '>= 1.4.1'
Then run bundle install.
4. Setup the controller
# app/controllers/oauth/inaturalist/callbacks_controller.rb
require 'oauth2'
module Oauth
module Inaturalist
class CallbacksController < ::ActionController::Base
# GET /oauth/inaturalist
def passthru
redirect_to client.auth_code.authorize_url
end
# This endpoint is where the provider redirects the user back to
# after authorization.
# GET /oauth/inaturalist/callback
def callback
# Fetch an auth token from the access code
token = client.auth_code.get_token(params[:code])
# Perform an authenticated request to get the users data
api_response = token.get("/users/edit.json")
#user_data = JSON.parse(api_response.body)
# This is just an example of how you can use the user data from
# the provider
#user = {
uid: #user_data["id"],
nickname: #user_data["nickname"]
}
session[:user_id] = #user[:uid]
session[:token] = token.to_hash
redirect_to root_path, success: "Hello #{#user[:nickname]}"
end
private
# Change this if you are not using Rails 5 credentials.
def client
OAuth2::Client.new(
credentials.fetch(:client_id),
credentials.fetch(:secret),
site: "https://www.inaturalist.org",
redirect_uri: oauth_inaturalist_callback_url
)
end
def credentials
Rails.application.credentials.fetch(:inaturalist)
end
end
end
end
token here is actually a new OAuth2::AccessToken instance that can be called to call endpoints with the fetched credentials.
This example stores the token in the session. You can retrieve it in subsequent requests with:
token = OAuth2::AccessToken.from_hash( session[:token] )
The docs kind of mention trading the oauth access token for an api token for api.inaturalist.org. But the details are kind of sparse.
5 Add a link to sign in:
<%= link_to 'Sign in to iNaturalist.org', oauth_inaturalist_authorize_path %>

Ruby OAuth2.0: client credential type has unsupported client authentication method

I am using OAuth2 gem, for making a client_credential authentication. My code is as below,
require 'oauth2'
client = OAuth2::Client.new("my_client_id", "my_client_secret", :site => "my_site_url", :token_url => "oauth2/token")
client.client_credentials.get_token
When I execute above code block, it respond with below error,
OAuth2::Error (invalid_client: Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method))
{
"error":"invalid_client","error_description":"Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method)",
"error_hint":"The OAuth 2.0 Client supports client authentication method "client_secret_basic", but method "client_secret_post" was requested.
You must configure the OAuth 2.0 client's "token_endpoint_auth_method" value to accept "client_secret_post".","status_code":401}
I checked the using 'net/http' library, and my client_id & client_secrets are valid and working.
The only problem I see is with the authentication method as said in hint of above message,
The OAuth 2.0 Client supports client authentication method "client_secret_basic", but method "client_secret_post" was requested. You must configure the OAuth 2.0 client's "token_endpoint_auth_method" value to accept "client_secret_post"
What I want to know is?
How OAuth2 gem decide on using client_secret_post vs client_secret_basic? I mean How can I request with client_secret_basic in OAuth2 gem?
If not above then, How should I specify token_endpoint_auth_method to accpet client_secret_post?
OK, so finally I cleared these points.
OAuth2 gem does make a request to OAuth server with --token_endpoint_auth_method set to 'client_secret_post'.
While registering an client with OAuth server we will have to set token_endpoint_auth_method to 'client_secret_post', so that it will work.
In my case I was using Hydra, so I used below command to create a client:
hydra clients create --endpoint <OAuth server url> --id CLIENT_ID --secret CLIENT_SECRET \
--token-endpoint-auth-method 'client_secret_post' -g client_credentials
Now, using these CLIENT_ID and CLIENT_SECRET with oauth2 works.
But still one point which is unclear - can I make a request with token_endpoint_auth_method set to client_secret_basic using oauth2 gem.
I also encountered the same issue.
Please add or change this client option setting in your client code.
:auth_scheme => :basic_auth
The default settings is below.
:auth_scheme => :request_body
I have excerpted a part of the OAuth2::Client code.
Please check it.
require 'faraday'
require 'logger'
module OAuth2
# The OAuth2::Client class
class Client # rubocop:disable Metrics/ClassLength
attr_reader :id, :secret, :site
attr_accessor :options
attr_writer :connection
# #option opts [Symbol] :auth_scheme (:basic_auth) HTTP method to use to authorize request (:basic_auth or :request_body)
def initialize(client_id, client_secret, options = {}, &block)
opts = options.dup
#id = client_id
#secret = client_secret
#site = opts.delete(:site)
ssl = opts.delete(:ssl)
#options = {:authorize_url => '/oauth/authorize',
:token_url => '/oauth/token',
:token_method => :post,
:auth_scheme => :request_body, # <-- Here !!!
:connection_opts => {},
:connection_build => block,
:max_redirects => 5,
:raise_errors => true}.merge(opts)
#options[:connection_opts][:ssl] = ssl if ssl
end
Sample snippet is here https://gist.github.com/mtoshi/cd74f57631805fb1b2290137f58dac9f
If you use a middleware it probably use the client_secret_basic to make a request you only need to change the configuration of that something similar to this. I use nextauth middleware.
client: {
token_endpoint_auth_method: 'client_secret_post'
}

Can I send requests to app with devise without credentials?

I have backend app with devise and a ionic app from which I make requests to backend.
Whenever I send requests, gets me 401 status unauthorized.
In this project I've already doorkeeper to manage authorization, so I don't rly need authorization from devise. Can I somehow make these requests( add something to headers ) always authorized without sending post request with credentials?
You need to identify the user somehow. If you're not using a session, you'll need to generate some kind of access token, which Doorkeeper can do for you. I'm not sure if this is helpful or not, but I set up the following flow recently.
One option when using OAuth2 through a trusted client, e.g. a front-end app you build/distribute yourself, is the Resource Owner Password Credentials Grant. Doorkeeper has a guide in the docs for this with advice on dealing with Devise.
I ended up with something like this in my Doorkeeper initializer. You don't need to authorize the client, because you trust it:
resource_owner_from_credentials do |routes|
request.params[:user] = {:email => request.params[:email], :password => request.params[:password]}
request.env['devise.allow_params_authentication'] = true
request.env['warden'].authenticate!(:scope => :user)
end
skip_authorization do |resource_owner|
true
end
Then you should be able to send a request using the password grant type as follows (also shown in the docs).
RestClient.post 'http://localhost:3000/oauth/token', {grant_type: 'password', email: 'email#gmail.com', password: 'password'}, {:accept => 'application/json'}
You should receive the same JSON back as shown in the docs.
{
"access_token": "1f0af717251950dbd4d73154fdf0a474a5c5119adad999683f5b450c460726aa",
"token_type": "bearer",
"expires_in": 7200
}
Now you have a way of identifying your user. You just attach this token to each request to endpoints protected by doorkeeper_for.
RestClient.get 'http://localhost/api/v1/users', { 'Authorization' => 'Bearer 1f0af717251950dbd4d73154fdf0a474a5c5119adad999683f5b450c460726aa', :accept => 'application/json'}

Ruby and Twitter: Getting Access Token from Request Token?

I am currently in Step 3 of the processing on getting an oauth token/secret from an user trying to login via Twitter. https://dev.twitter.com/docs/auth/implementing-sign-twitter
Step 3 tells me to send this request to the API, but I am stuck as to how to do so. I currently have BOTH the oauth_token and oauth_verifier, but how do I send this POST request to get the oauth_token, oauth_token_secret pair?
Is there a standard Oauth Ruby gem I can use to send this POST request? I see examples online where I pass an #accessToken object, but i do not have such an object available. I just have the oauth_token and oauth_verifier (as strings). Given these 2 things, how do I convert them to an oauth_token and oauth_token_secret?
POST /oauth/access_token HTTP/1.1
User-Agent: themattharris' HTTP Client
Host: api.twitter.com
Accept: */*
Authorization: OAuth oauth_consumer_key="cChZNFj6T5R0TigYB9yd1w",
oauth_nonce="a9900fe68e2573b27a37f10fbad6a755",
oauth_signature="39cipBtIOHEEnybAR4sATQTpl2I%3D",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1318467427",
oauth_token="NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0",
oauth_version="1.0"
Content-Length: 57
Content-Type: application/x-www-form-urlencoded
oauth_verifier=uw7NjWHT6OJ1MpJOXsHfNxoAhPKpgI8BlYDhxEjIBY
Try something like the following rails controller actions, using the twitter and oauth gems:
def redirect
consumer = OAuth::Consumer.new(CONSUMER_KEY, CONSUMER_SECRET, {
:site => "https://api.twitter.com",
:scheme => :header
})
request_token = consumer.get_request_token(:oauth_callback => CALLBACK_URL)
session[:twitter_request_token] = request_token
redirect_to request_token.authorize_url #=> "https://api.twitter.com/oauth/authorize?oauth_token=XYZ"
end
def callback
request_token = session[:twitter_request_token]
access_token = request_token.get_access_token(:oauth_verifier => params[:oauth_verifier])
client = Twitter::REST::Client.new(
:consumer_key => CONSUMER_KEY,
:consumer_secret => CONSUMER_SECRET,
:access_token => access_token.token,
:access_token_secret => access_token.secret
)
twitter_user = client.user
redirect_to root_url # or do something with the twitter_user
end
See also: http://barkingiguana.com/2009/10/13/twitter-oauth-authentication-using-ruby/
yes there is the Omniauth gem for authentication with Twitter. The documentation is straight forward.
I personally use Omniauth integrated with Devise and the Twitter gem to access Twitter - works very well.
Hope this helps,
Eugen
The common procedure is the following:
You shell to register your app on twitter development page.
Then set the proper Name, Description, and Website values up for your application.
App Name
App Description
http://your_app_domain.zone:3000/
Change Application Type is your app, by default it has read only access type.
Setup the callback URL for yuor application:
http://your_app_domain.zone:3000/auth/twitter/callback
Store the all keys, and secrets that are shewn on the OAuth tool twitter page:
Consumer key:
Consumer secret:
Access token:
Access token secret:
Setup route on your site with devise, or devise-like gem with the specified twitter keys, and secrets to enable authentication engine. The route list now shall include /auth/twitter path.
By going to http://your_app_domain.zone:3000/auth/twitter you will be redirected to twitter site, and dropped back to your site with passed oauth_token
But
You simple receive those keys, and secrets, and apply then in your app, avoiding the 6, and 7 points:
client = Twitter::REST::Client.new do |config|
config.consumer_key = "YOUR_CONSUMER_KEY"
config.consumer_secret = "YOUR_CONSUMER_SECRET"
config.access_token = "YOUR_ACCESS_TOKEN"
config.access_token_secret = "YOUR_ACCESS_SECRET"
end

POSTing in OAuth with client credentials with Doorkeeper

I've implemented a REST API and protected it with doorkeeper.
I've written a small client program to access it and it works fine using the resource owner credential flow.
Now I'm trying to implement a call using the client credentials flow. So I've followed the example in the link.
Everything works great when I'm using a GET request, but when I'm using a POST request, I'm getting a 401 Unauthorized. This is a call to a method that doesn't require a resource owner.
The only relevant thing I have in my API controller is:
doorkeeper_for :all
I haven't implemented any scopes or nothing of that kind (am I required to?).
My client code looks like this (exactly as in the example in github):
require 'rest-client'
require 'json'
client_id = 'my_client_id...'
client_secret = 'my_client_secret...'
response = RestClient.post 'http://localhost:3000/oauth/token', {
grant_type: 'client_credentials',
client_id: client_id,
client_secret: client_secret
}
token = JSON.parse(response)["access_token"]
# this line works great:
RestClient.get 'http://localhost:3000/api/v1/flights.json', { 'Authorization' => "Bearer #{token}" }
# this line always fails (401 Unauthorized):
RestClient.post 'http://localhost:3000/api/v1/flights.json', { 'Authorization' => "Bearer #{token}" }
Any idea what I may be doing wrong? Is there something special I should do in my application in order to enable the client credentials flow?
I figured it out. The problem was that I didn't use RestClient.post properly. The second parameter should be the payload and the third should be the header. It should be something like this:
RestClient.post 'http://localhost:3000/api/v1/flights.json', {}, { 'Authorization' => "Bearer #{token}" }

Resources