[GH-ISSUE #237] issue with refreshtokens #170

Closed
opened 2026-02-27 19:26:25 +03:00 by kerem · 7 comments
Owner

Originally created by @kasperkamperman on GitHub (Nov 8, 2021).
Original GitHub issue: https://github.com/jwilsson/spotify-web-api-php/issues/237

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 ]);
}
Originally created by @kasperkamperman on GitHub (Nov 8, 2021). Original GitHub issue: https://github.com/jwilsson/spotify-web-api-php/issues/237 Hi, I followed the [documentation on automatically refreshing tokens](https://github.com/jwilsson/spotify-web-api-php/blob/main/docs/examples/refreshing-access-tokens.md#automatically-refreshing-access-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 ]); } ```
kerem closed this issue 2026-02-27 19:26:25 +03:00
Author
Owner

@jwilsson commented on GitHub (Nov 9, 2021):

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.

<!-- gh-comment-id:964447562 --> @jwilsson commented on GitHub (Nov 9, 2021): Hey! Your expectations on the functionality are 100% correct, you can check out [the code](https://github.com/jwilsson/spotify-web-api-php/blob/646b49a10430883f16985704b0c19142f75eb5b0/src/SpotifyWebAPI.php#L126) 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.
Author
Owner

@kasperkamperman commented on GitHub (Nov 9, 2021):

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 https://github.com/jwilsson/spotify-web-api-php/issues/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;
        }
}
};
<!-- gh-comment-id:964478065 --> @kasperkamperman commented on GitHub (Nov 9, 2021): 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 https://github.com/jwilsson/spotify-web-api-php/issues/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; } } }; ```
Author
Owner

@jwilsson commented on GitHub (Nov 21, 2021):

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.

<!-- gh-comment-id:974797077 --> @jwilsson commented on GitHub (Nov 21, 2021): 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.
Author
Owner

@kasperkamperman commented on GitHub (Nov 22, 2021):

No problem. If I find a bit of time, I check from my side what could be going on.

<!-- gh-comment-id:975900870 --> @kasperkamperman commented on GitHub (Nov 22, 2021): No problem. If I find a bit of time, I check from my side what could be going on.
Author
Owner

@kasperkamperman commented on GitHub (Nov 26, 2021):

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).

<!-- gh-comment-id:979796083 --> @kasperkamperman commented on GitHub (Nov 26, 2021): 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).
Author
Owner

@kasperkamperman commented on GitHub (Nov 27, 2021):

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:
https://github.com/jwilsson/spotify-web-api-php/issues/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.

<!-- gh-comment-id:980534979 --> @kasperkamperman commented on GitHub (Nov 27, 2021): 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: https://github.com/jwilsson/spotify-web-api-php/issues/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.
Author
Owner

@jwilsson commented on GitHub (Nov 28, 2021):

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.

<!-- gh-comment-id:981079194 --> @jwilsson commented on GitHub (Nov 28, 2021): 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.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/spotify-web-api-php#170
No description provided.