web-dev-qa-db-fra.com

Google API Client "le jeton d'actualisation doit être transmis ou défini dans le cadre de setAccessToken"

Je suis actuellement confronté à un problème très étrange. En effet, je suis ce même guide ( https://developers.google.com/google-apps/calendar/quickstart/php ) de la documentation relative à l'API Google. Je l'ai essayé deux fois, la première fois que cela fonctionnait à merveille, mais après expiration du jeton d'accès, le script fourni directement par Google API n'a pas pu être actualisé.

TL; DR

Voici le message d'erreur:

sam@ssh:~$ php www/path/to/app/public/quickstart.php


Fatal error: Uncaught exception 'LogicException' with message 'refresh token must be passed in or set as part of setAccessToken' in /home/pueblo/www/path/to/app/vendor/google/apiclient/src/Google/Client.php:258
Stack trace:
#0 /home/pueblo/www/path/to/app/public/quickstart.php(55): Google_Client->fetchAccessTokenWithRefreshToken(NULL)
#1 /home/pueblo/www/path/to/app/public/quickstart.php(76): getClient()
#2 {main}
  thrown in /home/pueblo/www/path/to/app/vendor/google/apiclient/src/Google/Client.php on line 258

Voici la partie du script php de google que j'ai modifiée:

require_once __DIR__ . '/../vendor/autoload.php';

// I don't want the creds to be in my home folder, I prefer them in the app's root
define('APPLICATION_NAME', 'LRS API Calendar');
define('CREDENTIALS_PATH', __DIR__ . '/../.credentials/calendar-php-quickstart.json');
define('CLIENT_SECRET_PATH', __DIR__ . '/../client_secret.json');

J'ai aussi modifié la expandHomeDirectory pour pouvoir la "désactiver" sans trop modifier le code:

function expandHomeDirectory($path) {
  $homeDirectory = getenv('HOME');
  if (empty($homeDirectory)) {
    $homeDirectory = getenv('HOMEDRIVE') . getenv('HOMEPATH');
  }
  return $path;
  // return str_replace('~', realpath($homeDirectory), $path);
}

Donc, pour vérifier si je me trompais ou si Google l'était, j'ai fait une expérience: hier soir, j'ai lancé le script quickstart de ssh pour vérifier si cela fonctionnait et c'est ce qui s'est passé. J'ai donc décidé de vérifier ce matin si cela fonctionnait toujours. comme je le faisais avant de dormir et ce n'était pas le cas, je pense qu'il y a quelque chose qui cloche dans le quickstart.php de Google.

J'espère que quelqu'un pourra m'aider, j'ai déjà vérifié tous les autres articles sur le sujet, mais ils sont tous dépassés.

12
Sam

J'ai eu le même problème récemment et je l'ai résolu avec ceci.

<?php
 $client->setRedirectUri($this->_redirectURI);
 $client->setAccessType('offline');
 $client->setApprovalPrompt('force');

J'explique ......__ Le jeton d'actualisation n'est pas renvoyé car nous n'avons pas forcé l'approbationPrompt. Le mode hors connexion n'est pas suffisant. Nous devons forcer l'approbationPrompt. De plus, le redirectURI doit être défini avant ces deux options. Cela a fonctionné pour moi.

C'est ma fonction complète

<?php
     private function getClient()
     {
        $client = new Google_Client();
        $client->setApplicationName($this->projectName);
        $client->setScopes(SCOPES);
        $client->setAuthConfig($this->jsonKeyFilePath);
        $client->setRedirectUri($this->redirectUri);
        $client->setAccessType('offline');
        $client->setApprovalPrompt('force');

       // Load previously authorized credentials from a file.
       if (file_exists($this->tokenFile)) {
         $accessToken = json_decode(file_get_contents($this->tokenFile), 
         true);
      } else {
        // Request authorization from the user.
        $authUrl = $client->createAuthUrl();
        header('Location: ' . filter_var($authUrl, FILTER_SANITIZE_URL));

        if (isset($_GET['code'])) {
            $authCode = $_GET['code'];
            // Exchange authorization code for an access token.
            $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
            header('Location: ' . filter_var($this->redirectUri, 
            FILTER_SANITIZE_URL));
            if(!file_exists(dirname($this->tokenFile))) {
                mkdir(dirname($this->tokenFile), 0700, true);
            }

            file_put_contents($this->tokenFile, json_encode($accessToken));
        }else{
            exit('No code found');
        }
    }
    $client->setAccessToken($accessToken);

    // Refresh the token if it's expired.
    if ($client->isAccessTokenExpired()) {

        // save refresh token to some variable
        $refreshTokenSaved = $client->getRefreshToken();

        // update access token
        $client->fetchAccessTokenWithRefreshToken($refreshTokenSaved);

        // pass access token to some variable
        $accessTokenUpdated = $client->getAccessToken();

        // append refresh token
        $accessTokenUpdated['refresh_token'] = $refreshTokenSaved;

        //Set the new acces token
        $accessToken = $refreshTokenSaved;
        $client->setAccessToken($accessToken);

        // save to file
        file_put_contents($this->tokenFile, 
       json_encode($accessTokenUpdated));
    }
    return $client;
}
17
Ulrich Dohou

J'ai rencontré le même problème avec la nouvelle bibliothèque Google Api. Le lien suivant a été recherché par la recherche de solution: RefreshToken Ne pas être renvoyé après avoir reçu un nouveau jeton google sheets API

Sur la base de ces informations, j'ai modifié la partie de code de démarrage rapide pour répondre à mes besoins. Après une première autorisation auprès de Google, j'ai obtenu drive-php-quickstart.json qui contient refresh_token, qui expire dans 3 600 secondes ou une heure. Le jeton d'actualisation est émis une seule fois, donc s'il est perdu, une nouvelle autorisation est nécessaire . Donc, pour l'avoir toujours dans drive-php-quickstart.json, j'ai effectué ce qui suit:

// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
// save refresh token to some variable
$refreshTokenSaved = $client->getRefreshToken(); 

// update access token
$client->fetchAccessTokenWithRefreshToken($refreshTokenSaved); 

// pass access token to some variable
$accessTokenUpdated = $client->getAccessToken();

// append refresh token
$accessTokenUpdated['refresh_token'] = $refreshTokenSaved;

// save to file
file_put_contents($credentialsPath, json_encode($accessTokenUpdated)); 
}
4
Salavat Sayfullin

Mon conseil est enregistrer le jeton d'actualisation dans .json immédiatement après l'obtention du jeton d'accès et si le jeton d'accès a expiré, utilisez le jeton d'actualisation.

Dans mes projets fonctionnent de cette façon:

public static function getClient()
{
    $client = new Google_Client();
    $client->setApplicationName('JhvInformationTable');
    $client->setScopes(Google_Service_Calendar::CALENDAR_READONLY);
    $client->setAuthConfig('credentials.json');
    $client->setAccessType('offline');

    // Load previously authorized credentials from a file.
    $credentialsPath = 'token.json';
    $credentialsPath2 = 'refreshToken.json';
    if (file_exists($credentialsPath)) {
        $accessToken = json_decode(file_get_contents($credentialsPath), true);
    } else {
        // Request authorization from the user.
        $authUrl = $client->createAuthUrl();
        //printf("Open the following link in your browser:\n%s\n", $authUrl);
        //print 'Enter verification code: ';
        $authCode = trim(fgets(STDIN));

        //echo "<script> location.href='".$authUrl."'; </script>";
        //exit;

        $authCode ='********To get code, please uncomment the code above********';

        // Exchange authorization code for an access token.
        $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
        $refreshToken = $client->getRefreshToken();

        // Check to see if there was an error.
        if (array_key_exists('error', $accessToken)) {
            throw new Exception(join(', ', $accessToken));
        }

        // Store the credentials to disk.
        if (!file_exists(dirname($credentialsPath))) {
            mkdir(dirname($credentialsPath), 0700, true);
        }
        file_put_contents($credentialsPath, json_encode($accessToken));
        file_put_contents($credentialsPath2, json_encode($refreshToken));
        printf("Credentials saved to %s\n", $credentialsPath);
    }
    $client->setAccessToken($accessToken);

    // Refresh the token if it's expired.
    if ($client->isAccessTokenExpired()) {
        $refreshToken = json_decode(file_get_contents($credentialsPath2), true);
        $client->fetchAccessTokenWithRefreshToken($refreshToken);
        file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
    }
    return $client;
}
3
Michal Blažek

quelques mises à jour pour ceux qui ont des problèmes avec ce message, principalement parce que seule la première commande fetchAccessTokenWithAuthCode () génère des références contenant le jeton de rafraîchissement (techniquement valable pour toujours - aucune validité de 2 heures si vous ne le révoquez pas). Lorsque vous obtenez le nouveau, celui-ci remplace celui d'origine, mais il ne contient pas le jeton d'actualisation dont il a besoin. Par conséquent, il se bloquera à la prochaine mise à jour du jeton. Cela peut être facilement résolu en remplaçant la fonction d'actualisation, par exemple:

  // Refresh the token if it's expired.
  if ($client->isAccessTokenExpired()) {
    $oldAccessToken=$client->getAccessToken();
    $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
    $accessToken=$client->getAccessToken();
    $accessToken['refresh_token']=$oldAccessToken['refresh_token'];
    file_put_contents($credentialsPath, json_encode($accessToken));
}

Maintenant, chaque fois que vous mettez à jour le jeton d'accès, votre jeton d'actualisation est également transmis.

2
Therian

J'ai eu les mêmes problèmes et finalement aller au travail:

Backstory:

J'ai reçu la même erreur. Voici ce que j'ai trouvé:

Cette erreur:

Erreur irrécupérable PHP: exception logicielle non capturée: le jeton d'actualisation doit être transmis ou défini dans le cadre de setAccessToken in /Library/WebServer/Documents/Sites/test/scripts/vendor/google/apiclient/src/Google/Client.php : 267

Fait référence à la méthode du jeton d'accès à la mise à jour (aussi appelée Refresh):

$client->fetchAccessTokenWithRefreshToken($refreshTokenSaved);

Pourquoi a-t-il échoué? En résumé, j'ai réalisé en imprimant le tableau $ accessToken, qui provient du décodage de ce fichier json (d'après le code de démarrage rapide que vous avez posté/qui provient de Google).

informations d'identification/calendar-php-quickstart.json

J'ai trouvé l'erreur en raison de la façon dont le tableau accessToken est imprimé lorsque je print_r:

Tableau ( [code_accès] => Tableau ( [Code_accès] => xyz123 [Type de jeton] => Porteur [Expires_in] => 3600 [Refresh_token] => xsss112222 [Created] => 1511379484 )

)

Solution:

$ refreshToken = $ accessToken ["access_token"] ["refresh_token"];

juste avant cette ligne:

    $client->fetchAccessTokenWithRefreshToken($refreshToken);

Je peux enfin actualiser le jeton selon les besoins lorsqu'il expire dans une heure. Je pense que les développeurs de cet article ont supposé que le tableau était imprimé comme suit:

Tableau ( [access_token] => xyz123 [token_type] => Bearer [expires_in] => 3600 [refresh_token] => xsss112222 [created] => 1511379484 )

ils ont donc pensé que vous pourriez simplement faire $ accessToken ["refresh_token"]; Ce n'est pas correct. 

Nous avons maintenant une valeur valide pour $ refreshToken. L'erreur devrait disparaître si vous procédez ainsi. J'ai également mis à jour l'auteur via le lien de commentaires pour le lui faire savoir, au cas où d'autres développeurs php rencontreraient ce problème. Espérons que cela aide quelqu'un. Toutes mes excuses si j'ai mal formulé ce message, je suis nouveau dans S.E. Je voulais juste partager ce que j'ai finalement réussi à faire fonctionner.

1
user3760763

Vous devez sérialiser la accestoken lorsque vous l'écrivez dans la credentialsPath.

 // Exchange authorization code for an access token.
    $accessToken = $client->authenticate($authCode);

    // Store the credentials to disk.
    if(!file_exists(dirname($credentialsPath))) {
        mkdir(dirname($credentialsPath), 0700, true);
    }
    $serArray = serialize($accessToken);
    file_put_contents($credentialsPath, $serArray);
    printf("Credentials saved to %s\n", $credentialsPath);

Et lorsque vous lisez le fichier, vous devez le désérialiser.

if (file_exists($credentialsPath)) {
    $unserArray =  file_get_contents($credentialsPath);
    $accessToken = unserialize($unserArray);

}

Fonction complète

function getClient() {
    $client = new Google_Client();
    // Set to name/location of your client_secrets.json file.
    $client->setAuthConfigFile('client_secret.json');
    // Set to valid redirect URI for your project.
    $client->setRedirectUri('http://localhost');
    $client->setApprovalPrompt('force');

    $client->addScope(Google_Service_YouTube::YOUTUBE_READONLY);
    $client->setAccessType('offline');

    // Load previously authorized credentials from a file.
    $credentialsPath = expandHomeDirectory(CREDENTIALS_PATH);


    if (file_exists($credentialsPath)) {
        $unserArray =  file_get_contents($credentialsPath);
        $accessToken = unserialize($unserArray);

    } else {
        // Request authorization from the user.
        $authUrl = $client->createAuthUrl();
        printf("Open the following link in your browser:\n%s\n", $authUrl);
        print 'Enter verification code: ';
        $authCode = trim(fgets(STDIN));

        // Exchange authorization code for an access token.
        $accessToken = $client->authenticate($authCode);

        // Store the credentials to disk.
        if(!file_exists(dirname($credentialsPath))) {
            mkdir(dirname($credentialsPath), 0700, true);
        }
        $serArray = serialize($accessToken);
        file_put_contents($credentialsPath, $serArray);
        printf("Credentials saved to %s\n", $credentialsPath);
    }

    $client->setAccessToken($accessToken);

    // Refresh the token if it's expired.
    if ($client->isAccessTokenExpired()) {
        $client->refreshToken($client->getRefreshToken());
        file_put_contents($credentialsPath, $client->getAccessToken());
    }
    return $client;
}
1

Après un certain temps à voir ce code:

// Exchange authorization code for an access token.
// "refresh_token" is returned along with the access token
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);


// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
    $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
    file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}

il suffisait de ce changement:

// Exchange authorization code for an access token.
// "refresh_token" is returned along with the access token
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);


// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
    $client->fetchAccessTokenWithRefreshToken($accessToken);
    file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}

puisque cette fonction $ client-> getRefreshToken () renvoie null, et si vous fournissez directement $ accessToken, cela fonctionnera parfaitement et mettra à jour votre fichier, espérons qu'il résoudra le problème.

0
oron abutbul

Google a mis à jour son PHP Quickstart , avec une méthode améliorée pour gérer ceci:

Extrait ci-dessous:

// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);

// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
0
dxdc

Dans mon cas, j'avais oublié de définir le type d'accès sur "offline" sans lequel le jeton d'actualisation n'était pas généré.

$client->setAccessType('offline');

Une fois que cela est fait, l'exemple de code donné dans la documentation de Google fonctionnera.

// Exchange authorization code for an access token.
// "refresh_token" is returned along with the access token
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);


// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
    $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
    file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
0
jahackbeth