Can not send header to vimeo using HTTParty - ruby-on-rails

I want to get unauthenticated access token from vimeo api in my rails app. However the post request made using HTTParty gem returns following response from API
{"error"=>"You must provide a valid authenticated access token."}
The code to send request is
header = "basic " + Base64.encode64("****07974be" + ":" + "****ygYBI7I")
token = HTTParty.post("https://api.vimeo.com/oauth/authorize/client",
:body => {:grant_type => 'client_credentials'},
:header => {'Authorization' => header}
)
json=JSON.parse(token)
I have checked that credentials are correct and also tried replacing :header with :headers, and various combinations of using string instead of symbol in the header hash. But none of them works.
However, the call to same URL, using same credentials is successful through Postman.
Edit As mentioned in a answer, we need to use headers (plural) while making the call. However, I had already tried that but problem persists. Using basic_auth, instead of sending headers do seems to work, however I can not figure out why sending headers through HTTParty is not working but similar call is working through Postman.

The :headers option is definitely plural, but since you are using basic auth, you can also use HTTParty's basic auth option. So your request would become:
username = "YOUR-USER-HERE"
password = "YOUR-PASSWORD-HERE"
token = HTTParty.post("https://api.vimeo.com/oauth/authorize/client",
body: {:grant_type => 'client_credentials'},
basic_auth: { username: username, password: password }
)
Using your creds (did you mean to post real creds?) I got
{"access_token"=>"REDACTED", "token_type"=>"bearer", "scope"=>"public", "app"=>{"name"=>"Fable", "uri"=>"/apps/REDACTED"}}

Related

Authentication using Resource Owner Password Credentials in Rails

I am considerably new to OAuth authentication scheme. What pose a problem for me at the moment is getting access_token from server in Rails app.
So far I read few articles covering methods related to Resource Owner Password Credentials in OAuth 2.0, but still it got me nowhere.
To name a few Official documentation regarding ROPC / Introduction to OAuth2 / Description of OAuth2 gem from Intridea
Server that I want to connect with allows password grant. It's deployed by 3rd party, so I assume everything is ok with it. On manual page they defined example of authorization as follows:
curl -X POST -d
'grant_type=password&username=USER_EMAIL&password=YOUR_PASSWORD&client_id=CLIENT_ID&client_secret=CLIENT_SECRET'
'https://auth.example.com/oauth2/token'
I posses all data which is mentioned above. BTW, client_id and client_secret are generic values enclosed in documentation. Server uses Doorkeeper gem to implement OAuth2.
To retrieve access_token from server, I simply put advised by Doorkeeper's wiki code into one of my controllers. Testing ROPC for Doorkeeper
My code in Rails API app utilizing OAuth2 gem from Intridea:
def test
client = OAuth2::Client.new(CLIENT_ID, CLIENT_SECRET,
site: 'https://auth.example.com/oauth2/token')
access_token = client.password.get_token(username, password)
Rails.logger.info access_token.token
end
What I get after visiting localhost/test is Completed 500 Internal Server Error with OAuth2::Error saying that page that I look for doesn't exist.
When trying just use curl from command line with respective data, I recieve:
WWW-Authenticate: Bearer realm="Doorkeeper", error="invalid_grant", error_description="The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."
Please kindly advise what may cause problem in these set-up.
As it seems I overlooked one important thing, we should declare explicitly token_url in relation to site address and not treat site parameter as entire path.
So in order to request access_token my method should look like this:
def test
client = OAuth2::Client.new(client_id,
client_secret,
token_url: "/oauth2/token",
site: "https://www.example.com/" )
access_token = client.password.get_token(username, password)
Rails.logger.info access_token.token
end
Here you can find similar issue to mine.
If someone wants to get access token with password credentials using simple http method, here is example how to approach this thing:
def test
param = {
:client_id => client_id,
:client_secret => client_secret,
:grant_type => 'password',
:username => username,
:password => password
}
uri = URI.parse(service_url)
http = Net::HTTP.new(uri.host, uri.port)
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
http.use_ssl = true
request = Net::HTTP::Post.new(uri.request_uri)
request.body = param.to_query
response = http.request(request)
Rails.logger.info response.body()
end

Rails - Slack API OAuth Access - invalid_client_id

I'm building Slack integration for my Ruby on Rails application and I'm trying to get an access_token from the Slack API for my Slack App when a user clicks the Add to Slack button.
From Postman, I can successfully post the following:
https://slack.com/api/oauth.access?client_id=idgoes.here&client_secret=secretgoeshere&code=12345&pretty=1
However, within Rails I always get a response with invalid_client_id, regardless of the way I call the API. I have checked my ID is correct (a lot) and tried regenerating it, but I don't think that is the issue due to the postman success.
Within my get_oauth_access_token method I have tried the following implementations:
1.
rc = JSON.parse(HTTP.post('https://slack.com/api/oauth.access',
params: {
client_id: 'idgoes.here',
client_secret: 'secretgoeshere',
code: '12345'
}))
2.
response = Excon.post('https://slack.com/api/oauth.access',
headers: { 'Content-Type' => 'application/json; charset=utf-8' },
user: client_id, password: client_secret,
body: oauth_request_body.to_json)
Any implementation I try always ends up getting a invalid_client_id response.
I'm aware it may be something to do with environment config, but I'm not sure what would be helpful to debug, so please let me know what other information I can share. I'm running on localhost.
Update:
I just found out that many (maybe all) of the Slack APIs do not accept a JSON format body (which seems crazy seeing as they send a response in JSON.
Make sure to use x-www-form-urlencoded format body on your request or it will not work properly.
"Content-Type" => "application/x-www-form-urlencoded"
I use oauth2 gem to authorize. So I was able to get this to work by reading the slack documentation and using oauth2 in my controller:
class OauthController < ApplicationController
def authorize
options = {
site: 'https://slack.com/oauth/authorize'
}
client ||= OAuth2::Client.new(
'client-id',
'client-secret',
options
)
params = {
scope: 'incoming-webhook, commands',
redirect_uri: 'https://localhost:3000/oauth/callback'
}
redirect_to client.auth_code.authorize_url(params)
end
def authorize_callback
puts params["code"]
redirect_to root_url
end
end
Routes file:
get '/authorize', to: 'oauth#authorize'
get '/oauth/callback', to: 'oauth#authorize_callback'
Don't forget to set your callback url at Oauth settings on api.slack.com, I used localhost for testing purposes as you can see.

Issues sending a POST via Net::HTTP to a Battle.Net Community API End-Point (OAuth 2)

Versions:
Ruby 2.2.4
Rails 4.2
Omniauth-oauth2 1.3.1
Omniauth-bnet 1.1.0
Issue:
Trying to complete the authorization and token request process to Blizzard's Battle Net Community API. While I can get the authorization_code returned, when I attempt to construct a POST back to the token endpoint it keeps telling me that its an invalid request/internal server error or just returns back the following object: <Net::HTTPFound 302 Found readbody=true> which has a blank string for a response body. Details for how Blizzard recommends handling the OAuth 2 process are located here: Battle.net OAuth 2 Guide. The omniauth-bnet gem is the one Blizzard suggested but doesn't seem to handle the entire OAuth authorization and token process but I'll freely admit I'm brand new when it comes to anything OAuth related so I could be wrong.
Any help you all can provide would be very welcome!
Controller Code:
def index
client_id = ENV[BNET_CLIENT_ID]
client_secret = ENV[BNECT_CLIENT_SECRET]
uri = URI('https://us.battle.net/auth/token?
redirect_uri=https%3A%2F%2f127%2f0%2f0%2f1%3A3001%2Fauth%2Fbnet%2Fcallback
&grant_type=authorization_code
&code=' + params["code"])
req = Net::HTTP::Post.new(uri)
req.basic_auth(client_id, client_secret)
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') {|http| http.request(req)}
#bnet_response = res.body
end
Process:
Click link on index.html page which triggers the authorization process to start by sending a GET request to https://us.battle.net/oauth/authorize?client_id=&client_secret=&redirect_uri=&response_type=code
BNet API accepts the request and redirects user to Battle.Net Login screen
User authorizes app and is redirected (via redirect_uri) back to the index page with query parameters for the authorization_code
My app SHOULD make a POST request using the controller code above and the response should contain the actual access token.
This is just some hastily cobbled together code while I'm trying to get this working. I'll clean it up once I get over this little speed bump. I wouldn't hardcode the URI and such normally, just getting frustrated with things.
Things I've tried:
Using Rest-Client : There's a callback redirect that it can't handle apparently
Testing Postman : When using their OAuth 2 Authorization it works just fine, also works fine if I use Postman to get the authorization code (GET) and token (POST), so I'm assuming things work on the Blizz side and my code just sucks.
config/routes.rb
Rails.application.routes.draw do
root to: 'pages#index'
get '/auth/:provider/callback', to: 'pages#index'
end
I'm not familiar with the Battle.net API, however reading your process it seems you are getting an authorization code back from the redirect in the form of a query parameter (accessed by params[:code]) correct?
You also mention that this works using Postman indicating the flaw must be somewhere in the code.
I would suggest using the following snippet and let me know if it works. I would encourage using puts url after url = ... code to ensure the URI looks exactly as you want it (and has the correct code, client id, secret, redirect_uri).
require 'uri'
require 'net/http'
# Remember to change the redirect uri to match yours
redirect_uri = 'https://my-domain-name.com/auth/bnet/callback'
# Double check the environment variable names for BNET ID / Secret
url = URI("https://us.battle.net/oauth/token?redirect_uri=#{redirect_uri}&code=#{params[:code]}" \
"&grant_type=authorization_code&scope=wow.profile&client_id=#{ENV['BNET_CLIENT_ID']}" \
"&client_secret=#{ENV['BNET_CLIENT_SECRET']}")
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)
response = http.request(request)
puts response.read_body
Ensure your domain is accessible over https as this seems to be required.

Rails 4 - Google One Time Code Flow with omniauth-google-oauth2, keep getting "Invalid code"

I have a Rails app in which I want users to be able to sign in / up with Google. I'm using the following gem:
#gemfile
gem 'omniauth-google-oauth2'
I've almost got it to work (I actually received the access token once, not sure why) - but before getting the access_token I constantly get the following error:
"error"=>"invalid_grant", "error_description"=>"Invalid code."
I have checked so that the code is unique for each request and that it isn't nil. The relevant parts of the method where I try to get the access_token after I've received the one time authorisation code looks like this:
def google_authentication
respond_to do |format|
# authorisation code
code = params[:code]
unless code.blank?
client_id = ENV["GOOGLE_CLIENT_ID"]
client_secret = ENV["GOOGLE_CLIENT_SECRET"]
redirect_uri = 'postmessage'
grant_type = 'authorization_code'
load = {client_id: client_id, client_secret: client_secret, redirect_uri: redirect_uri, grant_type: grant_type, code: code}
payload = load.to_json
url = "https://www.googleapis.com/oauth2/v3/token"
response = HTTParty.post(url, :query => load)
json = JSON.parse(response.body)
unless json.nil?
unless json[:error].present?
# TODO: Handle data
format.json { render :json => {:message => "Success"} }
else
# ERROR "Invalid code" always happen
end
end
end
end
end
In Google's developer console I have the following credentials:
Client ID [CLient ID]
Email address [Email]
Client secret [Secret]
Redirect URIs http://127.0.0.1:3000/
JavaScript origins http://127.0.0.1:3000
Would be thankful for any ideas or tips.
Update for completion
This is how I set up omniauth-google-oauth2:
Rails.application.config.middleware.use OmniAuth::Builder do
provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_CLIENT_SECRET"],
{
:scope => "email, profile",
:prompt => "select_account",
:provider_ignores_state => true
}
end
Update 2
As mentioned above I once managed to get the access token once, I managed to reproduce it again. I did it by clicking my sign in button three times.The first time I got:
"error"=>"invalid_grant", "error_description"=>"Invalid code."
The second click resulted in:
"error"=>"invalid_grant", "error_description"=>"Code was already redeemed."
And the third time I successfully got the access_token. I find it really strange that I sometimes get the access_token, but most of the time get:
"error"=>"invalid_grant", "error_description"=>"Invalid code."
And the success / error "rate" isn't 100% consistent. Sometimes it takes more than three clicks. I find it quite strange that it sometimes works and that I sometimes get different error responses without changing anything in my code.
Could it be related to time / expiration date of the code?
Update 3
For additional completion. This is how my Javascript (or CoffeeScript) looks like when the user clicks the Google Sign in button:
$(document).ready ->
$.ajax
url: 'https://apis.google.com/js/client:plus.js?onload=gpAsyncInit'
dataType: 'script'
cache: true
window.gpAsyncInit = ->
$('.googleplus-login').click (e) ->
e.preventDefault()
gapi.auth.authorize {
immediate: false
response_type: 'code'
cookie_policy: 'single_host_origin'
client_id: '[id]'
scope: 'email profile'
}, (response) ->
if response and !response.error
jQuery.ajax
type: 'POST'
url: '/auth/google_oauth2/callback'
dataType: 'json'
data: response
success: (json) ->
# response from server
console.log "JSON: " + json
return
else
# google authentication failed
So ...as it is wrote:
Authorized redirect
URIs One URI per line. Needs to have a protocol,
no URL fragments, and no relative paths. Can't be a public IP Address.
Your setting:
Redirect URIs http://127.0.0.1:3000/
JavaScript origins http://127.0.0.1:3000/
...is wrong.
It should be:
Redirect URIs: http://my.true.domain/users/auth/google/callback
and
provider :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_CLIENT_SECRET"],
{
:scope => "email, profile",
:name => "google",
...
I hope it help!
I cant help with the ruby part but I may be able to help you figure out what's wrong.
There are in fact 3 codes returned by Google's authentication server. Authorization code, Access token, and refresh token.
Authorization code can only be used once to get the first refresh token.
Access Token used to access data on the apis, expires after an hour.
Refresh Token used to get a new access token when it expires. good until the user removes access.
Authorization code
This is the code that gets returned when the user hits accept to your application.
Example:
Change the client id, secret, and scope in this URI to the ones you are using. then paste it into a browser location bar.
https://accounts.google.com/o/oauth2/auth?client_id={clientid}.apps.googleusercontent.com&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope={scope}&response_type=code
It will prompt you for authentication. If you click except you get another window with a long code in it looking something like this.
That is the Authentication code, its only purpose in life is for you to use it to get an access token and a refresh token. It can only be used once, and its probably short lived though I have never tested how long they are good for.
In the uri above Note: the response type code.
Exchange:
Once you have that authentication code you need to exchange it for an access token and a refresh token. this is a HTTP POST so cant be placed in a browser window.
https://accounts.google.com/o/oauth2/token
code=4/X9lG6uWd8-MMJPElWggHZRzyFKtp.QubAT_P-GEwePvB8fYmgkJzntDnaiAI&client_id={ClientId}.apps.googleusercontent.com&client_secret={ClientSecret}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&grant_type=authorization_code
Note: grant_type=authorization_code this tells the server you are sending it an authorization code.
response
{
"access_token" : "ya29.1.AADtN_VSBMC2Ga2lhxsTKjVQ_ROco8VbD6h01aj4PcKHLm6qvHbNtn-_BIzXMw",
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : "1/J-3zPA8XR1o_cXebV9sDKn_f5MTqaFhKFxH-3PUPiJ4"
}
You now have an access token that can be used to access the Google APIs, it is short live lasts only 3600 seconds or 1 hour. After that you must use the refresh token to get access again.
Use refreshtoken
https://accounts.google.com/o/oauth2/token
client_id={ClientId}.apps.googleusercontent.com&client_secret={ClientSecret}&refresh_token=1/ffYmfI0sjR54Ft9oupubLzrJhD1hZS5tWQcyAvNECCA&grant_type=refresh_token
response
{
"access_token" : "ya29.1.AADtN_XK16As2ZHlScqOxGtntIlevNcasMSPwGiE3pe5ANZfrmJTcsI3ZtAjv4sDrPDRnQ",
"token_type" : "Bearer",
"expires_in" : 3600
}
Now that you understand all of that
"error"=>"invalid_grant", "error_description"=>"Code was already redeemed."
means that you are sending the authorization code again you can only use it once you should be sending the refresh token again. There is something up with your authentication flow. Again sorry I cant help with the ruby part.
code ripped from Google 3 legged oauth2 flow

Authenticating to an API with a token

I'm working with the Zendesk API, an HTTPS-only, JSON API and authentication is required to update a resource, and the API supplies an API token to use when authenticating as different users. When updating a resource, I issue a PUT request over SSL, assign the appropriate JSON content to the request body, and specify the Content-Type request header as application/json.
Next, the API instructs its users to authenticate as the end-user by either using the user's email and password (which I can't do for several reasons) or to use the user's email address along with the API token. The following is my attempt to authorize to the API with the Authorization header:
#id = params[:id]
#comment_body = params[:comment]
uri = URI.parse "https://{subdomain}.zendesk.com/api/v2/requests/#{#id}.json"
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
req = Net::HTTP::Put.new(uri.request_uri)
req.body = '{"request": {"comment":{"value":' + "\"#{#comment_body}\"" + '}}}'
req['Content-Type'] = 'application/json'
#The following two lines do not work!
credentials = Base64::encode64("{user_email}/token:{api_token}")
request.headers['Authorization'] = "Basic #{credentials}"
response = http.request(req)
The API specifies that the format for authentication using the API token is {user_email}/token:{api_token}. I encoded that format with Base64::encode64 and passed it to the Authorization Header preceded with Basic, but the response is a 401 Unauthorized. However, replacing those two lines with req.basic_auth {user_email}, {user_password} works fine.
So my question is, how can I authenticate as a different user using the email and the given API token as authentication instead of supplying the user's email and password to req.basic_auth?
The googling I've done on the topic has revealed very little; apparently it's a lot more common to use the normal {username}:{password} format when dealing with the Authorization header than an API token.
Thanks in advance!!
Update: Weirdly, trying to authenticate as the end-user with req['Authorization'] = "Basic #{credentials}" does not return a 401 Unauthorized Error or a WWW-Authenticate header while trying to authorize as request.headers['Authorize'] = "Basic #{credentials}" does.
Finally figured it out after much head-banging and nearly throwing my laptop out the window. Suddenly, the answer seems incredibly obvious.
When using Net::HTTP, its basic_auth method can also accept tokens depending on the API, and the Zendesk API specifies that the format for using the API token is {email}/token:{token}. Basic authentication uses the format {username}:{password}, where the two fields are separated by a colon, meaning in Zendesk's case I can place {email}/token as the first argument and {token} as the second argument (instead of the username as the first argument and the password as the second argument), so the following code is correct:
req.basic_auth "{email}/token", "{api_token}"
I hope anyone who found this useful could leave a comment. Nice to know I spared someone from this frustration.

Resources