Search code examples
ruby-on-railsrubyhttphttprequestrest-client

Spotify Web API: Basic authorization


I need to get an access token from Spotify Web API. Based on this documentation I wrote the following method:

def authorize
  grant = Base64.encode64("#{SPOTIFY_KEY}:#{SPOTIFY_SECRET}")
  RestClient::Request.execute(
    method: :post,
    url: 'https://accounts.spotify.com/api/token',
    params: {'grant_type' => 'client_credentials'},
    headers: {"Authorization" => "Basic #{grant}","Accept" => "*/*; q=0.5, application/json"}
  )
end

and the following RSpec test:

it 'authorize' do
  obj = SpotifyIntegration.new
  response = obj.authorize
  myjson = JSON.parse(response.body)
  expect(myjson.has_key?('access_token')).to be(true)
  expect(myjson.has_key?('token_type')).to be(true)
  expect(myjson['token_type']).to eq('bearer')
  expect(myjson.has_key?('expires_in')).to be(true)
end

It happens that when I run this test, generating this request (caught with RESTCLIENT_LOG=stdout)

RestClient.post "https://accounts.spotify.com/api/token", "Accept"=>"/; q=0.5, application/json", "Accept-Encoding"=>"gzip, deflate", "Authorization"=>"Basic Y2NmNTI3ODVlZWI1NDVlODk0ZmM2ZTY3YTZhNDM0ZDA6YTQ5MjdlOGFmOWQy\nNGE0OTgyZDRkODI1MmJhZjBkNTI=\n"

I get

=> 400 BadRequest | application/json 131 bytes

And it seems it is really a bad request, because I see no sign of the grant_type => client_credentials. The documentaions says this is mandadory as a request body parameter.

I believe I am sending this the wrong way, but I don't know how to to it correctly.

I tried to use RestClient#post instead of RestClient::Request#execute, doing this:

def authorize
  grant = Base64.encode64("#{SPOTIFY_KEY}:#{SPOTIFY_SECRET}")
  RestClient.post 'https://accounts.spotify.com/api/token', {'grant_type' => 'client_credentials'}.to_json, {"Authentication" => "Basic #{grant}",content_type: :json, accept: :json}
end

but then I got:

RestClient::UnsupportedMediaType: 415 Unsupported Media Type

How may I send a request body parameter using RestClient gem?


Solution

  • The issue is the way Base64 is encoding the string which includes newlines that most OAuth2 providers don't accept. You can do this instead:

    grant = Base64.encode64("#{client_id}:#{client_secret}").delete("\n")
    
    resp = RestClient.post('https://accounts.spotify.com/api/token',
                           {'grant_type' => 'client_credentials'},
                           {"Authorization" => "Basic #{grant}"})
    

    According to this answer, new lines are added every 60 characters (that is news to me). You can use another method that does not include newlines such as strict_encode...

    grant = Base64.strict_encode64("#{client_id}:#{client_secret}")