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 andNo 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.
The solution is to manually generate a Spotify refresh token then use that to create an access token when needed. Visit your Spotify developers dashboard then select or create your app. Note down your Client ID, Client Secret, and Redirect URI in a convenient location to use in Step 2.
By setting tokenSwapURL and tokenRefreshURL it is possible for the iOS-SDK to request a new access token with a refresh token whenever needed. The iOS-SDK demo project has a ruby example of the needed back-end services.
If a longer session is desired Spotify account service supports the OAuth Code grant flow. The iOS-SDK provides helper functionality to simplify the use of the Code grant flow. By setting tokenSwapURL and tokenRefreshURL it is possible for the iOS-SDK to request a new access token with a refresh token whenever needed.
Since the job runs in the background I needed a way to avoid the Spotify login pop-up during the authorization flow. The solution is to manually generate a Spotify refresh token then use that to create an access token when needed.
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With