8

tl;dr I'm receiving: JSON text did not start with array or object and option to allow fragments not set. if i'm trying to receive a token and No refresh token available in the session! if I'm trying to renew a token.

I'm trying to setup the token refresh for the Objective-C Spotify iOS SDK beta-25 in Swift 3. I'm using a Heroku Server and the Ruby script provided by Spotify, changed to my credentials.

require 'sinatra'
require 'net/http'
require 'net/https'
require 'base64'
require 'encrypted_strings'
require 'json'

CLIENT_ID = ENV['xxx']
CLIENT_SECRET = ENV['xxx']
ENCRYPTION_SECRET = ENV['xxx']
CLIENT_CALLBACK_URL = ENV['xxx://returnafterlogin']
AUTH_HEADER = "Basic " + Base64.strict_encode64(CLIENT_ID + ":" + CLIENT_SECRET)
SPOTIFY_ACCOUNTS_ENDPOINT = URI.parse("https://accounts.spotify.com")

get '/' do
"Working"    
end

post '/swap' do

    # This call takes a single POST parameter, "code", which
    # it combines with your client ID, secret and callback
    # URL to get an OAuth token from the Spotify Auth Service,
    # which it will pass back to the caller in a JSON payload.

    auth_code = params[:code]

    http = Net::HTTP.new(SPOTIFY_ACCOUNTS_ENDPOINT.host, SPOTIFY_ACCOUNTS_ENDPOINT.port)
    http.use_ssl = true

    request = Net::HTTP::Post.new("/api/token")

    request.add_field("Authorization", AUTH_HEADER)

    request.form_data = {
        "grant_type" => "authorization_code",
        "redirect_uri" => CLIENT_CALLBACK_URL,
        "code" => auth_code
    }

    response = http.request(request)

    # encrypt the refresh token before forwarding to the client
    if response.code.to_i == 200
        token_data = JSON.parse(response.body)
        refresh_token = token_data["refresh_token"]
        encrypted_token = refresh_token.encrypt(:symmetric, :password => ENCRYPTION_SECRET)
        token_data["refresh_token"] = encrypted_token
        response.body = JSON.dump(token_data)
    end

    status response.code.to_i
    return response.body
end

post '/refresh' do

    # Request a new access token using the POST:ed refresh token

    http = Net::HTTP.new(SPOTIFY_ACCOUNTS_ENDPOINT.host, SPOTIFY_ACCOUNTS_ENDPOINT.port)
    http.use_ssl = true

    request = Net::HTTP::Post.new("/api/token")

    request.add_field("Authorization", AUTH_HEADER)

    encrypted_token = params[:refresh_token]
    refresh_token = encrypted_token.decrypt(:symmetric, :password => ENCRYPTION_SECRET)

    request.form_data = {
        "grant_type" => "refresh_token",
        "refresh_token" => refresh_token
    }

    response = http.request(request)

    status response.code.to_i
    return response.body

end

Set by:

SPTAuth.defaultInstance().tokenSwapURL = URL(string: SpotifyCredentials.tokenSwapURLSwap)
SPTAuth.defaultInstance().tokenRefreshURL = URL(string: SpotifyCredentials.tokenSwapURLRefresh)

Now the user is not able to login anymore and I'm receiving the error posted on top. If I'm deleting tokenSwapURL and tokenRefreshURL, everything works again, but the User has to re-auth every 60 minutes.

If I'm trying to refresh the Token with an already logged in user, I receive:

"No refresh token available in the session!"

if SPTAuth.defaultInstance().session != nil {
        print("needs login")
        SPTAuth.defaultInstance().renewSession(SPTAuth.defaultInstance().session, callback: { error, session in
            if error != nil {
                print("\(error?.localizedDescription)") // "No refresh token available in the session!"
                return
            }
        })
}

What am I missing? Help is very appreciated.

Arnab
  • 3,267
  • 1
  • 26
  • 39
David Seek
  • 15,533
  • 14
  • 94
  • 125

2 Answers2

3

I have been able to create a token refresh service for Spotify with the following Git:

https://github.com/adamontherun/SpotifyTokenRefresh

All you need to do is to follow the instructions of the Heroku link within the git project.

enter image description here

I have tried to get in contact with the author of the project, but he wasn't able to tell me, why my approach wasn't working but his is. All I can leave you with is this working Deploy to Heroku link.

David Seek
  • 15,533
  • 14
  • 94
  • 125
  • 1
    I wanted to extend this, but I as I havn't programmed in ruby before I decided to put together a service that does an identical thing but using `Node.js` : https://github.com/alfiedouglas0/spotify-token-refresh – Alfie Aug 30 '19 at 09:58
1

Short summary: assuming your JSON parsing works properly, the problem is malformed JSON (server-side).


JSON text did not start with array or object and option to allow fragments not set

can be thrown by 's JSONSerialization.jsonObject(with:options:) which returns

A Foundation object from the JSON data in data, or

nil if an error occurs.

The malformed JSON » nil instead of token » current token gets nilled » result: "the user is not able to login anymore"

Possible explanations for getting malformed JSON include:

It usually is because of some warning message throwing out from your server without putting it in the response array. For example in PHP, some "warning messages" are not caught in your array so that when you finally use "echo json_encode($RESPONSE_ARR)," it is not a JSON format. — https://stackoverflow.com/a/38680699/3419541

From the same SO page:

You need to debug this in the iOS application. First convert the data to a string and print that and check it. If the string looks alright then print the data itself - sometimes people manage to add 0 bytes or control characters, or two byte order markers or something similar which are invisible in the string but are not legal JSON. — https://stackoverflow.com/a/38681179/3419541

Community
  • 1
  • 1
PDK
  • 1,230
  • 11
  • 24
  • I have tried to follow your answer, but I kinda can't because everything is working in the spotify Framework. I do not handle any kind of JSON, all I do is get the spotify API the information if I want to run with or without refresh service and the server url. That's literally it. Everything else is handled by spotify. – David Seek Dec 06 '16 at 23:35