Search code examples
google-oauthaccess-tokenrefresh-token

Access Token client->refreshToken stopped working in long-running script


I have a script that replicates, on the local filesystem, changes that are made on a particular Google Drive folder and its subfolders. Script has been working well for a few days, but at noon today, the refresh code stopped working without any explanation that I can find.

Here's a log that demonstrates the issue

2023-08-11 08:00:03 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116361) - nothing to do.
2023-08-11 09:00:02 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116361) - nothing to do.
2023-08-11 10:00:02 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116361) - nothing to do.
2023-08-11 11:00:02 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116361) - nothing to do.
2023-08-11 12:00:02 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116361) - nothing to do.
2023-08-11 13:00:02 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116361) - nothing to do.
2023-08-11 14:00:02 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116361) - nothing to do.
2023-08-11 15:00:02 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116361) - nothing to do.
2023-08-11 16:00:03 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116361) - nothing to do.
2023-08-11 17:00:03 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116361) - nothing to do.
2023-08-11 18:00:02 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116361) - nothing to do.
2023-08-11 19:00:02 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116361) - nothing to do.
2023-08-11 20:00:03 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116361) - nothing to do.
2023-08-11 21:00:02 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116361) - nothing to do.
2023-08-11 21:45:02 /www/htdocs/gdrive/catchupSongBook: Downloading /SongBook/2023-08-12.lst (452 bytes)
2023-08-11 21:45:03 /www/htdocs/gdrive/catchupSongBook: Finished.  1 files found in 0 folders.  (1 files updated, 0 added.  0 new folders).  452 bytes downloaded.
2023-08-11 22:00:03 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116363) - nothing to do.
2023-08-11 23:00:03 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116363) - nothing to do.
2023-08-12 04:00:03 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116363) - nothing to do.
2023-08-12 05:00:02 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116363) - nothing to do.
2023-08-12 06:00:06 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116363) - nothing to do.
2023-08-12 07:00:02 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116363) - nothing to do.
2023-08-12 07:49:02 /www/htdocs/gdrive/catchupSongBook: Downloading /SongBook/2023-08-12.lst (452 bytes)
2023-08-12 07:49:02 /www/htdocs/gdrive/catchupSongBook: Finished.  1 files found in 0 folders.  (1 files updated, 0 added.  0 new folders).  452 bytes downloaded.
2023-08-12 08:00:03 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116366) - nothing to do.
2023-08-12 09:00:02 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116366) - nothing to do.
2023-08-12 10:00:03 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116366) - nothing to do.
2023-08-12 11:00:02 /www/htdocs/gdrive/catchupSongBook: No change in start page token (116366) - nothing to do.
[12-Aug-2023 12:00:02 America/New_York] PHP Fatal error:  Uncaught TypeError: array_key_exists(): Argument #2 ($array) must be of type array, null given in /www/cgi-bin/GoogleRelated/DriveStuff.php:160
Stack trace:
#0 /www/cgi-bin/GoogleRelated/DriveStuff.php(82): DriveStuff->GetAuth()
#1 /www/htdocs/gdrive/catchupSongBook(52): DriveStuff->GetDriveService()
#2 {main}
  thrown in /www/cgi-bin/GoogleRelated/DriveStuff.php on line 160

Here is the code in question. Comment about line 160 is added for clarity.

    $client->setAccessType($accessType);
    $this->GetClientFromDB($clientId        // Handles inserttion of new Client information if necessary; returns as much information as we have otherwise
                        , $clientSecret
                        , $scopes_dummy     // Values below here (including this value) are passed by reference, and are updated by GetClientFromDB()
                        , $accessToken      // Updated by GetClientFromDB()
                        , $tokenType        // Updated by GetClientFromDB()
                        , $refreshToken     // Updated by GetClientFromDB()
                        , $expires          // Updated by GetClientFromDB()
                        , $created          // Updated by GetClientFromDB()
                        , $rowId            // Updated by GetClientFromDB()
                            ) ;
    $authHistoryRow = [] ;
    $authHistoryRow['authConfig'] = json_encode($client_json) ;
    $authHistoryRow['clientId'] = $clientId ;
    $authHistoryRow['clientSecret'] = $clientSecret ;
    $authHistoryRow['projectId'] = $client_json[$topKey]['project_id'] ;
    $authHistoryRow['authUri'] = $client_json[$topKey]['auth_uri'] ;
    $authHistoryRow['tokenUri'] = $client_json[$topKey]['token_uri'] ;
    $authHistoryRow['authProviderX509CertUrl'] = $client_json[$topKey]['auth_provider_x509_cert_url'] ;
    $authHistoryRow['appName'] = $appName ;
    $authHistoryRow['scopes'] = implode(',', $scopes) ;
    $authHistoryRow['accessType'] = $accessType ;
    if ((! $rowId) || $client->isAccessTokenExpired()) {
        $accessToken = null ;
        // Refresh the token...
        if ($rowId && $client->isAccessTokenExpired() && $refreshToken) {
            $authHistoryRow['refreshToken'] = $refreshToken ;
            $client->refreshToken($refreshToken) ;
            $accessTokenArray = $client->getAccessToken() ;
(line 160 follows)
            $accessToken = (array_key_exists('access_token', $accessTokenArray) ? $accessTokenArray['access_token'] : false) ;
            $expires = $accessTokenArray['expires_in'] + time() ;
            $authHistoryRow['accessToken'] = $accessTokenArray['access_token'] ;
            $authHistoryRow['expires'] = Date('Y/m/d H:i:s', $expires) ;
            }
        if ((! $accessToken) ) { // Still not able to get authorization.  Talk to user.
            printf("Authorization expired at %s\n", date('Y-m-d H:i', $expires)) ;
            $authUrl = $client->createAuthUrl();
            printf("Open this link in your browser:\n%s\n", $authUrl);
            printf("Copy verification code\nOr, if 'Page cannot displayed' (or similar), copy entire URL from browser window.\nPaste here: ");
            $authCode = trim(fgets(STDIN));
            if (preg_match('%^[:/0-9.a-zA-Z_-]+[/][?]code=([^&]+)&%', $authCode, $matches))
                $authCode = urldecode($matches[1]) ;
            // Exchange authorization code for an access token.
            $accessTokenArray = $client->fetchAccessTokenWithAuthCode($authCode);
            //$output = print_r($accessToken, true) ;
            //file_put_contents("access_token.txt", $output) ;
            }
        $accessExpiresEpoch = time() + $accessTokenArray['expires_in'] ;

Of course it's likely that this code could apparently be improved in this refresh area (suggestions welcome!), but in any case, it seems(?) the service is not refreshing the token, and I would like to understand why. How do I find out the cause of $client->getAccessToken() returning null after a refreshToken() operation? What tests can I perform to ensure the refreshToken() operation actually worked, or why not if not?

An option might be to get a new set of tokens - I can easily take those steps, with some certainty of success. But more to the point is why it's not working before I do that. We don't want manual intervention every few days for a process that should just run.


Solution

  • if your app is in testing the refresh token expire after seven days.

    go to Google cloud console for your project under the consent screen and set it to production

    Publish app

    enter image description here

    Status now say in production

    enter image description here

    Single user apps do not need to be verified.

    video

    How to fix Invalid_grant and expiring refresh token