POSTing in OAuth with client credentials with Doorkeeper - ruby-on-rails

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}" }

Related

Newsletter2go ruby API client authorisation with get_token method

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

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'
}

HTTParty get request using Basic Auth

I am trying to make get request to this address https://unicom24.ru/api/partners/requests/v1/locality/
I am using httparty gem. I need to use basic http Auth.
I got my auth key and do request like this:
headers = {'Authorization' => 'Basic bMyAuthKeydv4K'}
HTTParty.get(
"https://unicom24.ru/api/partners/requests/v1/locality/",
:headers => headers
)
And I get a reply:
#response=#<Net::HTTPUnauthorized 401 Unauthorized readbody=true>
unallowable email or password.
What do I do?
HTTParty basic auth
auth = {username: 'user', password: 'pass'}
result = HTTParty.get(
"https://unicom24.ru/api/partners/requests/v1/locality/",
basic_auth: auth
)

GoDaddy redirect from office365 not returning refresh token

I have an app which syncs to OneDrive. If the user is using Office365 via GoDaddy and I have a grant_type of 'refresh_token', it doesn't return the refresh_token back, which in turn, won't let me refresh the token I currently have. I've tried adding access_type="offline" and prompt="consent" when doing a POST request to no avail. Help?
Here's my code:
credentials = OpenStruct.new
params = {
client_id: client_credentials[:key],
redirect_uri: redirect_url,
client_secret: client_credentials[:secret],
refresh_token: refresh_token,
grant_type: 'refresh_token',
resource: resource_id,
access_type: 'offline',
prompt: 'consent'
}
RestClient.post(client.token_url, params) # doesn't return refresh_token
Based on the request, it seems you were refresh the token. Based on the OAuth 2.0 code grant flow, there is no parameter about access_type and prompt. You can refer below for the support parameter:
And here is the post for your reference:
POST /{tenant}/oauth2/token HTTP/1.1
Host: https://login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&refresh_token=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq...
&grant_type=refresh_token
&resource=https%3A%2F%2Fservice.contoso.com%2F
&client_secret=JqQX2PNo9bpM0uEihUPzyrh

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'}

Resources