issue with refreshtokens
Closed this issue · 7 comments
Hi,
I followed the documentation on automatically refreshing tokens, however the auto_refresh function doesn't seem to work.
At least I expected that if a call can't be executed, the auto-refresh kicks in, refreshes the token and does the call again?
There are no new restrictions on my Spotify client app (created way before 2021-05-27).
I get this error (newest version of this php api):
Fatal error: Uncaught SpotifyWebAPI\SpotifyWebAPIAuthException: Invalid client in /home/u32728p27865/domains/kasperkamperman.com/public_html/dev/playmix/vendor/jwilsson/spotify-web-api-php/src/Request.php:56
Stack trace:
#0 /home/u32728p27865/domains/kasperkamperman.com/public_html/dev/playmix/vendor/jwilsson/spotify-web-api-php/src/Request.php(242): SpotifyWebAPI\Request->handleResponseError('{"error":"inval...', 400)
#1 /home/u32728p27865/domains/kasperkamperman.com/public_html/dev/playmix/vendor/jwilsson/spotify-web-api-php/src/Request.php(110): SpotifyWebAPI\Request->send('POST', 'https://account...', 'grant_type=refr...', 'HTTP/2 400 \r\nda...')
#2 /home/u32728p27865/domains/kasperkamperman.com/public_html/dev/playmix/vendor/jwilsson/spotify-web-api-php/src/Session.php(206): SpotifyWebAPI\Request->account('POST', '/api/token', Array, Array)
#3 /home/u32728p27865/domains/kasperkamperman.com/public_html/dev/playmix/vendor/jwilsson/spotify-web-api-php/src/SpotifyWebAPI.php(127): SpotifyWebAPI\Session->refreshAccessToken in /home/u32728p27865/domains/kasperkamperman.com/public_html/dev/playmix/vendor/jwilsson/spotify-web-api-php/src/Request.php on line 56
I know it's not the clientid and if I refresh my token in another way (logging in in my service, store it in the database), it works good for an hour (after which the refresh token becomes invalid).
My code ($spotifyAuth are the tokens obtained from the database).
$session = new SpotifyWebAPI\Session(
'CLIENT_ID',
'CLIENT_SECRET'
);
if ($spotifyAuth['sp_access_token']) {
echo "set access token\n";
$session->setAccessToken($spotifyAuth['sp_access_token']);
} else {
// Or request a new access token
echo "get a new access token!\n";
$session->refreshAccessToken($spotifyAuth['sp_refresh_token']);
}
if ($spotifyAuth['sp_refresh_token']) {
echo "set refresh token\n";
$session->setRefreshToken($spotifyAuth['sp_refresh_token']);
}
$options = [
"return_assoc" => true,
"auto_refresh" => true,
"auto_retry" => true
];
$api = new SpotifyWebAPI\SpotifyWebAPI($options,$session);
$api->setSession($session);
print_r($api->me());
/* UPDATE TOKENS IF NEEDED --------------------------------------------------- */
$newAccessToken = $session->getAccessToken();
if($debug) echo "\n new access: ".$newAccessToken."\n";
if(strcmp($newAccessToken, $spotifyAuth['sp_access_token']) !==0) {
if($debug) echo "access token updated\n";
$query = 'UPDATE users SET sp_access_token = ? WHERE id='.$spotifyAuth['user_id'];
$stmt = $spotifyAuth['pdo']->prepare($query);
$stmt->execute([ $newAccessToken ]);
}
$newRefreshToken = $session->getRefreshToken();
// print_r($spotifyAuth);
// echo $newRefreshToken;
if(strcmp($newRefreshToken, $spotifyAuth['sp_refresh_token']) !==0) {
if($debug) echo "refresh token updated\n";
$query = 'UPDATE users SET sp_refresh_token = ? WHERE id='.$spotifyAuth['user_id'];
$stmt = $spotifyAuth['pdo']->prepare($query);
$stmt->execute([ $newRefreshToken ]);
}
Hey!
Your expectations on the functionality are 100% correct, you can check out the code if you're interested.
However, I'm afraid I can't reproduce the issue you're having. I've tried both the Authorization Code and PKCE flows (unsure which one you're using but figured it should be one of those) with two different apps created before and after 2021-05-27 and it always refreshes the access token automatically.
I'd love to help out but I'm really stumped at the moment. If I come up with any other thoughts or ideas I'll be sure to get back to you.
Yes I have a stored accessToken and a refreshToken obtained with the Authorization code flow.
I reverted my code to my older implementation (see this issue #129) by wrapping the calls to the api in a function (don't mind the exeption variable wrongly written :) ).
I'll see if I can find some time to further debug it.
function callSpotify(&$api, &$spotifyAuth, $method, $args) {
try {
return call_user_func_array(array($api, $method), $args);
}
catch (\Exception $e) {
$exeptionCode = $e->getCode();
// The access token expired or Invalid access token
if($exeptionCode == 401) {
// Obtain new accessToken
$session = new SpotifyWebAPI\Session(CLIENT_ID, CLIENT_SECRET);
// decided that we grab the refresh from the table if it's empty.
// so we won't store it in a session for playlists (mostly accessTokens will be renewed in the cron).
if(empty($spotifyAuth['sp_refresh_token'])) {
$query = 'SELECT sp_refresh_token FROM users WHERE id=?';
$stmt = $spotifyAuth['pdo']->prepare($query);
$stmt->execute( [ $spotifyAuth['user_id'] ] );
$refreshToken = $stmt->fetchColumn();
$session->refreshAccessToken($refreshToken);
}
else $session->refreshAccessToken($spotifyAuth['sp_refresh_token']);
$new_access_token = $session->getAccessToken();
// store the newly received token
// we also store it in session or auth. Because the accessToken can be changed.
// in the cron or in the editor.
// no access_token passed, so we have a session
if(empty($spotifyAuth['sp_access_token'])) {
$_SESSION['auth']['sp_access_token'] = $new_access_token;
$query = 'UPDATE users SET sp_access_token = ? WHERE id='.$_SESSION['auth']['user_id'];
}
else {
$spotifyAuth['sp_access_token'] = $new_access_token;
$query = 'UPDATE users SET sp_access_token = ? WHERE id='.$spotifyAuth['user_id'];
}
$api->setAccessToken($new_access_token);
$stmt = $spotifyAuth['pdo']->prepare($query);
$stmt->execute([ $new_access_token ]);
unset($session);
$session = NULL;
return callSpotify($api, $spotifyAuth, $method, $args);
}
else if ($exeptionCode == 429) { // 429 is Too Many Requests
//if($debug) echo $e->getCode();
$lastResponse = $api->getRequest()->getLastResponse(); // Note "getRequest()" since $api->getLastResponse() won't be set
// Number of seconds to wait before sending another request, round up
$retryAfter = $lastResponse['headers']['Retry-After']+1;
sleep($retryAfter);
return callSpotify($api, $spotifyAuth, $method, $args);
}
else if ($exeptionCode == 403) {
// this error might run if we would like to update a playlist that's already deleted
// we don't have access anymore. We can solve this by following again.
// and hope we won't get stuck in a 403 loop....
if($method == 'updatePlaylist') {
callSpotify($api, $spotifyAuth, 'followPlaylistForCurrentUser', $args);
return callSpotify($api, $spotifyAuth, $method, $args);
}
else return null;
}
else {
// Some other kind of error
//echo $e->getMessage().'<br/>';
return null;
}
}
};
Hi again!
Sorry for not getting back to you. I did have another look but I still can't reproduce it. I compared the implementation in the library to your "old" one and I can't see anything obvious that could be the issue. I'd really like to solve it but I'm at a total loss.
No problem. If I find a bit of time, I check from my side what could be going on.
Just in progress of debugging this issue. I came across a change of error message when you set the auto_refresh to true.
$options = [
"return_assoc" => true,
"auto_retry" => true,
];
$api = new SpotifyWebAPI\SpotifyWebAPI();
$api->setOptions($options);
$api->setSession($session);
print_r($api->me());
This code throws:
Uncaught SpotifyWebAPI\SpotifyWebAPIException: The access token expired in...
When adding auto_refresh the error message changes:
$options = [
"return_assoc" => true,
"auto_retry" => true,
"auto_refresh" => true,
];
$api = new SpotifyWebAPI\SpotifyWebAPI();
$api->setOptions($options);
$api->setSession($session);
print_r($api->me());
Now the error reads:
Uncaught SpotifyWebAPI\SpotifyWebAPIAuthException: Invalid client in
It's strange that the error messages change when you set the auto-refresh option.
Of course the one without the option set is much more informative about what is causing the error, than the "invalid client" one.
When I update the accessToken, I get the expected output (so my personal information).
I'll check now if the refresh token indeed automatically refreshes (after a few hours).
Finally I found the issue.
I defined the secret constant variables like this:
define('CLIENT_ID', "17....");
define('CLIENT_SECRET', "64....");
After that I used your code example:
$session = new SpotifyWebAPI\Session(
'CLIENT_ID',
'CLIENT_SECRET'
);
So running this: echo "secret: ".$session->getClientSecret()."\n";
I got: 'CLIENT_ID'
I should have called it like:
$session = new SpotifyWebAPI\Session(
CLIENT_ID,
CLIENT_SECRET
);
So without the apostrophe.
I think it went wrong because I based my original auth implementation on this issue code:
#100 So in progress switching to your new implementation I overlooked this.
--
So now everything seems to work.
I leave it up to you to close this.
Although error messages were different, it was indeed an invalid client configuration after all.
Awesome! I'm so glad we got it figured out. Closing this issue but please don't hesitate to open a new one if there's any issues with the auto refresh functionality.