Spotify Web API Bad Request Error "invalid_client" when refreshing token - ruby-on-rails

I'm building an app in Rails using the Spotify web API. I built a method to refresh a user's token, but am receiving a 400 error. According the the Spotify Web API docs, the header of my request needs to be in the following format:
Authorization: Basic <base64 encoded client_id:client_secret>
Using Httparty gem, here's the POST method to refresh the access token:
def refresh_token
client_id = "foo"
client_secret = "bar"
client_id_and_secret = Base64.encode64("#{client_id}:#{client_secret}")
result = HTTParty.post(
"https://accounts.spotify.com/api/token",
:body => {:grant_type => "refresh_token",
:refresh_token => "#{self.oauth_refresh_token}"},
:headers => {"Authorization" => "Basic #{client_id_and_secret}"}
)
end
Here's what "result" ends up being:
=> #<HTTParty::Response:0x7f92190b2978 parsed_response={"error"=>"invalid_client", "error_description"=>"Invalid client secret"}, #response=#<Net::HTTPBadRequest 400 Bad Request readbody=true>, #headers={"server"=>["nginx"], "date"=>["Sun, 31 Aug 2014 22:28:38 GMT"], "content-type"=>["application/json"], "content-length"=>["70"], "connection"=>["close"]}>
I can decode client_id_and_secret and it returns "foo:bar", so I'm at a loss as to why I'm receiving a 400 error. Any insight is much appreciated.

Found the issue... it was with the Base64 encoding in Ruby. Apparently (as shown in Strange \n in base64 encoded string in Ruby) using the Base64.encode64('') method adds an extra line within the code. Using Base64.strict_encode64('') solved the issue.
Updated code:
def refresh_token
client_id = "foo"
client_secret = "bar"
client_id_and_secret = Base64.strict_encode64("#{client_id}:#{client_secret}")
result = HTTParty.post(
"https://accounts.spotify.com/api/token",
:body => {:grant_type => "refresh_token",
:refresh_token => "#{self.oauth_refresh_token}"},
:headers => {"Authorization" => "Basic #{client_id_and_secret}"}
)
end

Related

POST request timing out on request to APN server

I'm trying to set up Apple Push Notifications through a Rails back-end. I'm using the NetHttp2 gem to create the connection and jwt to sign my tokens.
Here's a sample of the responses I get when trying various combinations of post requests. I get a quick response if I omit the "authorization" header, and similar if I give it garbage or a fake token. But if I use the actual token that I've encoded, the connection times out. I have no idea what Apple is doing on the back-end - I get no message at all.
[22] pry(main)> request = client.call(:post, '/3/device/adknerulkdf2345nl',
body: "{ 'aps' => { 'hello' => 'hello' }", headers: { "apns-topic" => "something" })
=> #<NetHttp2::Response:0x007fbee8226c90 #body="{\"reason\":\"MissingProviderToken\"}", #headers={":status"=>"403", "apns-id"=>"690A959F-27F0-5B5C-1173-0316D6CE4C91"}>
[23] pry(main)> request = client.call(:post, '/3/device/adknerulkdf2345nl',
body: "{ 'aps' => { 'hello' => 'hello' }", headers: { 'authorization' => "bearer garbage", "apns-topic" => "something" })
=> #<NetHttp2::Response:0x007fbeebbbf060 #body="{\"reason\":\"InvalidProviderToken\"}", #headers={":status"=>"403", "apns-id"=>"8EF335C7-1F05-1D99-5425-CCF200960626"}>
[24] pry(main)> request = client.call(:post, '/3/device/adknerulkdf2345nl',
body: "{ 'aps' => { 'hello' => 'hello' }", headers: { 'authorization' => "bearer snwekrwunlsdfu.sdlfknweru.awepi234np2", "apns-topic" => "something" })
=> #<NetHttp2::Response:0x007fbee7f38f88 #body="{\"reason\":\"InvalidProviderToken\"}", #headers={":status"=>"403", "apns-id"=>"C3B48281-C7F1-22B0-6159-0FD0B79C1D43"}>
[25] pry(main)> request = client.call(:post, '/3/device/adknerulkdf2345nl',
body: "{ 'aps' => { 'alert' => 'hello' }", headers: { 'authorization' => "bearer #{token}", "apns-topic" => "something" })
SocketError: Socket was remotely closed
How do I debug this? Is there a way to view what is happening on the Apple server?
If not, has anyone set this up and has some tips? Honestly, I am not at all sure I am properly encoding the JWT token. I am attempting to use my p8 token from Apple and am using the 'ES256' settings but I don't know how to validate it. If I make another request with my token + garbage, it also hangs forever.
Here's the rest of the relevant code:
client = NetHttp2::Client.new("https://api.development.push.apple.com:443",
{ connect_timeout: 100000})
token = JWT.encode({'alg': 'ES256', 'kid': APNS_KEY_ID},
(OpenSSL::PKey::EC.new #p8key), 'ES256',
{'iss': TEAM_ID, 'iat': DateTime.now().to_time.to_i})
After much heartache, the solution
I'm still not totally sure what was going on with Apple, but the answer was with my token encoding. The proper format is:
token = JWT.encode(
{
"iss": TEAM_ID,
"iat": DateTime.now().to_time.to_i
},
private_key,
ALGORITHM,
header_fields= {
"alg": ALGORITHM,
"kid": APNS_KEY_ID
}
)
It seems if you give Apple something with the format authorization => bearer WHATEVER it will hang forever trying to authenticate.
The JWT library is very particular about order and keys (as of 6/29/17).

Ruby on Rails - SoundCloud OAuth 2 undefined method `access_token'

I am working on a making a personal soundcloud app on Rails 5 and i'm having some trouble with OAuth. I'm able to redirect to Soundcloud's app and give permission to my user. When I try to exchange the code for token on redirect_uri, get an error. I have included my code and image of the error I'm getting below.
def connected
client = Soundcloud.new(:client_id => 'My ID',
:client_secret => 'my secret',
:redirect_uri => "http://localhost:3000/login/soundcloud/callback")
code = params[:code]
value = client.exchange_token(:code => code) #get an error on this line
#my code to save access token into db goes here.
end
I have added this image
for the error that I'm getting. I thought it might be more helpful.
I was having the same issues, so I just did created the request manually for this step. Soundcloud gem is pretty dated, I'm sure it has something to do with that.
#client = Soundcloud.new(client_id: client_id,
client_secret: client_secret,
redirect_uri:'http://localhost:3000/soundcloud/connected')
#code = params[:code]
response = HTTParty.post("https://api.soundcloud.com/oauth2/token",
body: {
"client_id" => client_id,
"client_secret" => client_secret,
"redirect_uri" => 'http://localhost:3000/soundcloud/connected',
'grant_type'=> 'authorization_code',
"code" => #code
}
)
access_token = response["access_token"]
#authed_client = Soundcloud.new(access_token: access_token)
soundcloud_user = #authed_client.get('/me')
end
Hope this helps.

Google Drive API watch 400 error Ruby on Rails

I'm implementing the google drive api using the OAuth2 gem (ruby on rails). I'm not using the client library because I'm also integrating other API's, so I'm trying to make all these calls as modular as possible. I'm having trouble with the this request: POST https://www.googleapis.com/drive/v2/changes/watch.
I keep getting this error:
{"errors"=>[{"domain"=>"global", "reason"=>"required", "message"=>"entity.resource"}],
"code"=>400, "message"=>"entity.resource"}: { "error": { "errors": [ { "domain":
"global", "reason": "required", "message": "entity.resource" } ], "code": 400,
"message": "entity.resource" } }
which is not very useful. It may not be Google. It could be OAuth2, but I don't think so, because the debugger gets to the response after making the connection. Well, at this point, I don't know anything, so any help is appreciated. There is THIS GUY who has exactly the same error code as me, as well as the same conclusion.
Anyway, the relevant code parts:
First, the parameters I pass to OAuth2::AccessToken's post method(I need more than 10 rep to post another link, but here is the dochttp://rdoc.info/github/intridea/oauth2/ebe4be038ec14b349682/OAuth2/AccessToken#post-instance_method)(you can click on the request method to see how the params are handled)
base_url = request.protocol + request.host_with_port
channel_id = (0...50).map { ('a'..'z').to_a[rand(26)] }.join
body_post = {:id => channel_id,:type => 'web_hook',:address => base_url + "/googledrive/webhook"}
headers = {'Content-Type' => 'application/json'}
response = makeApiCall(token,"google_drive","/changes/watch","post",{},body_post,headers)
All this does is built my request with my the appropriate request body and headers for the call
Here is the relevant part from makeApiCall (at the line token.post is where the request is made, and where it breaks)
params = {"oauth_consumer_key" => ENV[key], "access_token" => token.token}.merge(params)
#body = Rack::Utils.build_query(body)
opts = {
:params => params,
:body => body,
:headers => headers
}
if(method=="get")
response = token.get(base + path,:opts => opts)
elsif(method=="post")
debugger
response = token.post(base + path,:opts => opts)
end
This is my first or second post, so forgive me if I messed anything up.
Okay, after struggling for so long, I figured out why it wasn't working. I forgot to do this:
JSON.generate(body_post)
So that my body was actually in application/json form.
Also, this line:
response = token.post(base + path,:opts => opts)
should be this:
response = token.post(base + path,opts)
simply because I'm not assigning the opts key, but the variable that I'm passing. Silly me.

Rails RestClient pass token in header fails

I do this curl in Terminal which works excellent:
$ curl https://myurl.com/api/v1/orders/53e0ae7f6630361c46060000 -H "Authorization: Token xxxxxxxxxxxxxxxxxxxxxx"
Output is json.
Now I want to access the json string via my rails app. I have tried RestClient to do this, but somehow I always get a 401 unauthorized error. I believe the token gets not send correctly via header. I have tried the following:
RestClient.get 'https://myurl.com/api/v1/orders/53e0ae7f6630361c46060000', {token: 'xxxxxxxxxxxxxxxxxxxxxx'}
and
RestClient.get 'https://myurl.com/api/v1/orders/53e0ae7f6630361c46060000', :params => {:token => 'xxxxxxxxxxxxxxxxxxxxxx'}
with no success. Maybe I use a wrong syntax for sending the token in the header?
Doku is here http://rubydoc.info/github/rest-client/rest-client - I could not find any mistakes.
# GET request with modified headers
RestClient.get 'http://example.com/resource', {:Authorization => 'Bearer cT0febFoD5lxAlNAXHo6g'}
# POST request with modified headers
RestClient.post 'http://example.com/resource', {:foo => 'bar', :baz => 'qux'}, {:Authorization => 'Bearer cT0febFoD5lxAlNAXHo6g'}
# DELETE request with modified headers
RestClient.delete 'http://example.com/resource', {:Authorization => 'Bearer cT0febFoD5lxAlNAXHo6g'}
Source: https://github.com/rest-client/rest-client#headers

Failed to open stream: HTTP request failed! HTTP/1.0 400 Bad Request - OAuth 2.0 POST

I am working with YouTube APIs for my college project, and I keep getting an error. Here I send them to the authorisation page to log in, when they allow access it sends the $_GET['code'] string back. Then I send this along with some other data and it should send back a JSON object. Instead I am just getting
Warning: file_get_contents(https://accounts.google.com/o/oauth2/token)
[function.file-get-contents]: failed to open stream: HTTP request
failed! HTTP/1.0 400 Bad Request in http://www.example.net/callback.php on line 27
I have replaced my domain with example.net just for security
urlencode($_GET['code']),
'client_id' => urlencode('111522767640.apps.googleusercontent.com '),
'client_secret' => urlencode('secret'),
'redirect_uri' => urlencode('http://example.net/callback.php'),
'grant_type' => urlencode('authorization_code')
)
);
$params =
array('http' =>
array(
'method' => 'POST /o/oauth2/token HTTP/1.1',
'header' => 'Host: accounts.google.com\r\n'.
'Content-Type: application/x-www-form-urlencoded',
'content' => $postdata
)
);
$context = stream_context_create($params);
$result = file_get_contents('https://accounts.google.com/o/oauth2/token', false,$context);
var_dump($_SESSION);
var_dump($result);
}
else //If code isnt set, user must have come here erroniously or has denied access to this program
{
//header( 'Location: www.example.net/error.php' ) ;
}
?>
file_get_contents is going to make a GET request to the url specified, but oauth2/token needs a POST request.
See reference Google OAuth2, PHP HTTP.
if you are using oauth2, goto libraries/oauth2/provider.php and uncomment the code line 182 shows
$ci = get_instance();
$ci->load->spark('curl/1.2.1');
$ci->curl
->create($url)
->post($params, array('failonerror' => false));
$response = $ci->curl->execute();
Here's how to do the Google oAuth properly:
$config = (object) array(
'CLIENT_ID' => 'AAAAA',
'CLIENT_SECRET' => 'BBBBB',
'REFRESH_TOKEN' => 'CCCCC',
);
$sJSON = file_get_contents('https://www.googleapis.com/oauth2/v4/token',FALSE,
stream_context_create(array('http'=>array(
'ignore_errors' => TRUE, // see errors in response instead of empty on error
'method' => 'POST',
'header' => array(
'Content-Type: application/x-www-form-urlencoded'
),
'content' => http_build_query(array(
'grant_type' => 'refresh_token',
'client_id' => $config->CLIENT_ID,
'client_secret' => $config->CLIENT_SECRET,
'refresh_token' => $config->REFRESH_TOKEN
))
)))
);
You can then use json_decode() to parse $sJSON into an object and then retrieve the access_token property.
For those who are wondering how to get the CLIENT_ID, CLIENT_SECRET, and REFRESH_TOKEN, watch this video. It's not easy. In my case, I needed to do this for Google Adwords API. So, I had to get my Developer Token and Login Customer ID from https://ads.google.com/home/tools/manager-accounts. Then, I had to go to https://console.developers.google.com/apis/credentials with this guide to generate a Client ID and Client Secret. Then, I followed this guide to learn how to get my Refresh Token.

Resources