web-dev-qa-db-fra.com

Comment actualiser un jeton avec le client Google API?

Je me suis amusé avec l'API Google Analytics (V3) et j'ai rencontré quelques erreurs. Tout d’abord, tout est configuré correctement et fonctionne avec mon compte de test. Mais lorsque je souhaite récupérer des données d'un autre ID de profil (compte Google Accont identique/Google identique), je reçois une erreur 403. Ce qui est étrange, c’est que les données de certains GA comptes renverront des données, tandis que d’autres génèrent cette erreur.

J'ai révoqué le jeton et authentifié une fois de plus, et maintenant, il semble que je puisse récupérer les données de tous mes comptes. Problème résolu? Ne pas. Comme la clé d’accès expirera, je reviendrai sur le même problème.

Si j'ai bien compris les choses, on pourrait utiliser le resfreshToken pour obtenir un nouveau authenticationTooken.

Le problème est, quand je cours:

$client->refreshToken(refresh_token_key) 

l'erreur suivante est renvoyée:

Error refreshing the OAuth2 token, message: '{ "error" : "invalid_grant" }'

J'ai vérifié le code derrière la méthode refreshToken et ai suivi la demande dans le fichier "apiOAuth2.php". Tous les paramètres sont envoyés correctement. Grant_type est durement codé pour "refresh_token" dans la méthode, alors il m'est difficile de comprendre ce qui ne va pas. Le tableau de paramètres ressemble à ceci:

Array ( [client_id] => *******-uqgau8uo1l96bd09eurdub26c9ftr2io.apps.googleusercontent.com [client_secret] => ******** [refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY [grant_type] => refresh_token )

La procédure est la suivante.

$client = new apiClient();
$client->setClientId($config['oauth2_client_id']);
$client->setClientSecret($config['oauth2_client_secret']);
$client->setRedirectUri($config['oauth2_redirect_uri']);
$client->setScopes('https://www.googleapis.com/auth/analytics.readonly');
$client->setState('offline');

$client->setAccessToken($config['token']); // The access JSON object.

$client->refreshToken($config['refreshToken']); // Will return error here

Est-ce un bug ou ai-je complètement mal compris quelque chose?

86
seorch.me

Alors j'ai finalement compris comment faire cela. L'idée de base est que vous avez le jeton que vous obtenez la première fois que vous demandez une authentification. Ce premier jeton a un jeton d'actualisation. Le premier jeton original expire au bout d'une heure. Au bout d'une heure, vous devez utiliser le jeton d'actualisation du premier jeton pour obtenir un nouveau jeton utilisable. Vous utilisez $client->refreshToken($refreshToken) pour récupérer un nouveau jeton. J'appellerai ce "jeton temp". Vous devez également stocker ce jeton temporaire car, une heure plus tard, il expire également et notez qu'aucun jeton d'actualisation ne lui est associé. Pour obtenir un nouveau jeton temporaire, vous devez utiliser la méthode que vous avez utilisée précédemment et utiliser le paramètre refreshtoken du premier jeton. J'ai attaché le code ci-dessous, qui est moche, mais je suis nouveau à cela ...

//pull token from database
$tokenquery="SELECT * FROM token WHERE type='original'";
$tokenresult = mysqli_query($cxn,$tokenquery);
if($tokenresult!=0)
{
    $tokenrow=mysqli_fetch_array($tokenresult);
    extract($tokenrow);
}
$time_created = json_decode($token)->created;
$t=time();
$timediff=$t-$time_created;
echo $timediff."<br>";
$refreshToken= json_decode($token)->refresh_token;


//start google client note:
$client = new Google_Client();
$client->setApplicationName('');
$client->setScopes(array());
$client->setClientId('');
$client->setClientSecret('');
$client->setRedirectUri('');
$client->setAccessType('offline');
$client->setDeveloperKey('');

//resets token if expired
if(($timediff>3600)&&($token!=''))
{
    echo $refreshToken."</br>";
    $refreshquery="SELECT * FROM token WHERE type='refresh'";
    $refreshresult = mysqli_query($cxn,$refreshquery);
    //if a refresh token is in there...
    if($refreshresult!=0)
    {
        $refreshrow=mysqli_fetch_array($refreshresult);
        extract($refreshrow);
        $refresh_created = json_decode($token)->created;
        $refreshtimediff=$t-$refresh_created;
        echo "Refresh Time Diff: ".$refreshtimediff."</br>";
        //if refresh token is expired
        if($refreshtimediff>3600)
        {
            $client->refreshToken($refreshToken);
        $newtoken=$client->getAccessToken();
        echo $newtoken."</br>";
        $tokenupdate="UPDATE token SET token='$newtoken' WHERE type='refresh'";
        mysqli_query($cxn,$tokenupdate);
        $token=$newtoken;
        echo "refreshed again";
        }
        //if the refresh token hasn't expired, set token as the refresh token
        else
        {
        $client->setAccessToken($token);
           echo "use refreshed token but not time yet";
        }
    }
    //if a refresh token isn't in there...
    else
    {
        $client->refreshToken($refreshToken);
        $newtoken=$client->getAccessToken();
        echo $newtoken."</br>";
        $tokenupdate="INSERT INTO token (type,token) VALUES ('refresh','$newtoken')";
        mysqli_query($cxn,$tokenupdate);
        $token=$newtoken;
        echo "refreshed for first time";
    }      
}

//if token is still good.
if(($timediff<3600)&&($token!=''))
{
    $client->setAccessToken($token);
}

$service = new Google_DfareportingService($client);
74
Uri Weg

Le problème est dans le jeton d'actualisation:

[refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY

Quand une chaîne avec un '/' obtient json encoded, Il s’échappe avec un '\', vous devez donc l'enlever.

Le jeton d'actualisation dans votre cas devrait être:

1/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY

Ce que je suppose, c'est que vous avez imprimé la chaîne JSON que Google a renvoyée et copié et collé le jeton dans votre code, car si vous json_decode alors il supprimera correctement le '\' pour vous!

41
Asim

voici l'extrait de code pour définir le jeton, avant cela, assurez-vous que le type d'accès doit être défini sur hors connexion

if (isset($_GET['code'])) {
  $client->authenticate();
  $_SESSION['access_token'] = $client->getAccessToken();
}

Pour actualiser le jeton

$google_token= json_decode($_SESSION['access_token']);
$client->refreshToken($google_token->refresh_token);

cela va actualiser votre jeton, vous devez le mettre à jour en session pour que vous puissiez le faire

 $_SESSION['access_token']= $client->getAccessToken()
18
Strik3r

Le type d'accès doit être défini sur offline. state est une variable que vous avez définie pour votre propre usage, pas celle de l'API.

Assurez-vous que vous avez le dernière version de la bibliothèque du client et ajoutez:

$client->setAccessType('offline');

Voir Formation de l'URL pour une explication des paramètres.

17
jk.

La réponse postée par @ uri-weg a fonctionné pour moi, mais comme je n'ai pas trouvé ses explications très claires, laissez-moi le reformuler un peu.

Lors de la première séquence d'autorisation d'accès, dans le rappel, lorsque vous atteignez le point où vous recevez un code d'authentification, vous devez également enregistrer le jeton d'accès et le jeton d'actualisation.

La raison en est que Google Api vous envoie un jeton d'accès avec un jeton d'actualisation uniquement lorsque vous demandez une autorisation d'accès. Les prochains jetons d'accès seront envoyés sans aucun jeton d'actualisation (sauf si vous utilisez le approval_Prompt=force option).

Le jeton d'actualisation que vous avez reçu la première fois reste valable jusqu'à ce que l'utilisateur révoque l'autorisation d'accès.

En php simpliste, un exemple de la séquence de rappel serait:

// init client
// ...

$authCode = $_GET['code'];
$accessToken = $client->authenticate($authCode);
// $accessToken needs to be serialized as json
$this->saveAccessToken(json_encode($accessToken));
$this->saveRefreshToken($accessToken['refresh_token']);

Et plus tard, en php simpliste, la séquence de connexion serait:

// init client
// ...

$accessToken = $this->loadAccessToken();
// setAccessToken() expects json
$client->setAccessToken($accessToken);

if ($client->isAccessTokenExpired()) {
    // reuse the same refresh token
    $client->refreshToken($this->loadRefreshToken());
    // save the new access token (which comes without any refresh token)
    $this->saveAccessToken($client->getAccessToken());
}
14
Daishi

Voici le code que j'utilise dans mon projet et qui fonctionne bien:

public function getClient(){
    $client = new Google_Client();
    $client->setApplicationName(APPNAME);       // app name
    $client->setClientId(CLIENTID);             // client id
    $client->setClientSecret(CLIENTSECRET);     // client secret 
    $client->setRedirectUri(REDIRECT_URI);      // redirect uri
    $client->setApprovalPrompt('auto');

    $client->setAccessType('offline');         // generates refresh token

    $token = $_COOKIE['ACCESSTOKEN'];          // fetch from cookie

    // if token is present in cookie
    if($token){
        // use the same token
        $client->setAccessToken($token);
    }

    // this line gets the new token if the cookie token was not present
    // otherwise, the same cookie token
    $token = $client->getAccessToken();

    if($client->isAccessTokenExpired()){  // if token expired
        $refreshToken = json_decode($token)->refresh_token;

        // refresh the token
        $client->refreshToken($refreshToken);
    }

    return $client;
}
7
Mr_Green

Avait le même problème; mon scénario qui a fonctionné hier, pour une raison étrange ne l’a pas été aujourd’hui. Aucun changement.

Apparemment, cela était dû au fait que mon horloge système était éteinte de 2,5 secondes (!!), en synchronisant avec NTP l'a corrigé.

Voir aussi: https://code.google.com/p/google-api-php-client/wiki/OAuth2#Solving_invalid_grant_errors

6
strikernl

FYI: L'API Google Analytics 3.0 actualisera automatiquement le jeton d'accès si vous en avez un à son expiration afin que votre script n'ait jamais besoin de refreshToken.

(Voir la fonction Sign dans auth/apiOAuth2.php)

3
Mark Smith

Parfois, le jet d’actualisation n’est pas généré à l’aide de $client->setAccessType ("offline");.

Essaye ça:

$client->setAccessType ("offline");
$client->setApprovalPrompt ("force"); 
3
Meenu Sharma

J'ai utilisé l'exemple des codes à puce avec la version actuelle de l'API Google, mais celle-ci ne fonctionnait pas. Je pense que son API est trop obsolète.

Donc, je viens d'écrire ma propre version, basée sur l'un des exemples d'API ... Elle génère le jeton d'accès, le jeton de demande, le type de jeton, le jeton d'ID, le délai d'expiration et le délai de création sous forme de chaînes

Si vos informations d'identification client et votre clé de développeur sont correctes, ce code devrait fonctionner immédiatement.

<?php
// Call set_include_path() as needed to point to your client library.
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_Oauth2Service.php';
session_start();

$client = new Google_Client();
$client->setApplicationName("Get Token");
// Visit https://code.google.com/apis/console?api=plus to generate your
// oauth2_client_id, oauth2_client_secret, and to register your oauth2_redirect_uri.
$oauth2 = new Google_Oauth2Service($client);

if (isset($_GET['code'])) {
    $client->authenticate($_GET['code']);
    $_SESSION['token'] = $client->getAccessToken();
    $redirect = 'http://' . $_SERVER['HTTP_Host'] . $_SERVER['PHP_SELF'];
    header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
    return;
}

if (isset($_SESSION['token'])) {
    $client->setAccessToken($_SESSION['token']);
}

if (isset($_REQUEST['logout'])) {
    unset($_SESSION['token']);
    $client->revokeToken();
}
?>
<!doctype html>
<html>
    <head><meta charset="utf-8"></head>
    <body>
        <header><h1>Get Token</h1></header>
        <?php
        if ($client->getAccessToken()) {
            $_SESSION['token'] = $client->getAccessToken();
            $token = json_decode($_SESSION['token']);
            echo "Access Token = " . $token->access_token . '<br/>';
            echo "Refresh Token = " . $token->refresh_token . '<br/>';
            echo "Token type = " . $token->token_type . '<br/>';
            echo "Expires in = " . $token->expires_in . '<br/>';
            echo "ID Token = " . $token->id_token . '<br/>';
            echo "Created = " . $token->created . '<br/>';
            echo "<a class='logout' href='?logout'>Logout</a>";
        } else {
            $authUrl = $client->createAuthUrl();
            print "<a class='login' href='$authUrl'>Connect Me!</a>";
        }
        ?>
    </body>
</html>
2
John Slegers

Cela fonctionne très bien ici, cela pourrait peut-être aider n'importe qui:

index.php

session_start();

require_once __DIR__.'/client.php';

if(!isset($obj->error) && isset($_SESSION['access_token']) && $_SESSION['access_token'] && isset($obj->expires_in)) {
?>
<!DOCTYPE html>
<html>
<head>
<title>Google API Token Test</title>
<meta charset='utf-8' />
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script>
search('Music Mix 2010');
function search(q) {
    $.ajax({
        type: 'GET',
        url: 'action.php?q='+q,
        success: function(data) {
            if(data == 'refresh') location.reload();
            else $('#response').html(JSON.stringify(JSON.parse(data)));
        }
    });
}
</script>
</head>
<body>
<div id="response"></div>
</body>
</html>
<?php
}
else header('Location: '.filter_var('https://'.$_SERVER['HTTP_Host'].dirname($_SERVER['PHP_SELF']).'/oauth2callback.php', FILTER_SANITIZE_URL));
?>

oauth2callback.php

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

session_start();

$client = new Google_Client();
$client->setAuthConfigFile('auth.json');
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->setRedirectUri('https://'.filter_var($_SERVER['HTTP_Host'].$_SERVER['PHP_SELF'], FILTER_SANITIZE_URL));
$client->addScope(Google_Service_YouTube::YOUTUBE_FORCE_SSL);

if(isset($_GET['code']) && $_GET['code']) {
    $client->authenticate(filter_var($_GET['code'], FILTER_SANITIZE_STRING));
    $_SESSION['access_token'] = $client->getAccessToken();
    $_SESSION['refresh_token'] = $_SESSION['access_token']['refresh_token'];
    setcookie('refresh_token', $_SESSION['refresh_token'], time()+60*60*24*180, '/', filter_var($_SERVER['HTTP_Host'], FILTER_SANITIZE_URL), true, true);
    header('Location: '.filter_var('https://'.$_SERVER['HTTP_Host'].dirname($_SERVER['PHP_SELF']), FILTER_SANITIZE_URL));
    exit();
}
else header('Location: '.filter_var($client->createAuthUrl(), FILTER_SANITIZE_URL));
exit();

?>

client.php

// https://developers.google.com/api-client-library/php/start/installation
require_once __DIR__.'/vendor/autoload.php';

$client = new Google_Client();
$client->setAuthConfig('auth.json');
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->addScope(Google_Service_YouTube::YOUTUBE_FORCE_SSL);

// Delete Cookie Token
#setcookie('refresh_token', @$_SESSION['refresh_token'], time()-1, '/', filter_var($_SERVER['HTTP_Host'], FILTER_SANITIZE_URL), true, true);

// Delete Session Token
#unset($_SESSION['refresh_token']);

if(isset($_SESSION['refresh_token']) && $_SESSION['refresh_token']) {
    $client->refreshToken($_SESSION['refresh_token']);
    $_SESSION['access_token'] = $client->getAccessToken();
}
elseif(isset($_COOKIE['refresh_token']) && $_COOKIE['refresh_token']) {
    $client->refreshToken($_COOKIE['refresh_token']);
    $_SESSION['access_token'] = $client->getAccessToken();
}

$url = 'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token='.urlencode(@$_SESSION['access_token']['access_token']);
$curl_handle = curl_init();
curl_setopt($curl_handle, CURLOPT_URL, $url);
curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 2);
curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl_handle, CURLOPT_USERAGENT, 'Google API Token Test');
$json = curl_exec($curl_handle);
curl_close($curl_handle);

$obj = json_decode($json);

?>

action.php

session_start();

require_once __DIR__.'/client.php';

if(isset($obj->error)) {
    echo 'refresh';
    exit();
}
elseif(isset($_SESSION['access_token']) && $_SESSION['access_token'] && isset($obj->expires_in) && isset($_GET['q']) && !empty($_GET['q'])) {
    $client->setAccessToken($_SESSION['access_token']);
    $service = new Google_Service_YouTube($client);
    $response = $service->search->listSearch('snippet', array('q' => filter_input(INPUT_GET, 'q', FILTER_SANITIZE_SPECIAL_CHARS), 'maxResults' => '1', 'type' => 'video'));
    echo json_encode($response['modelData']);
    exit();
}
?>
1
user1768700

J'ai un même problème avec google/google-api-client-php v2.0.0-RC7 et après une recherche d'une heure, j'ai résolu ce problème en utilisant json_encode comme ceci:

    if ($client->isAccessTokenExpired()) {
        $newToken = json_decode(json_encode($client->getAccessToken()));
        $client->refreshToken($newToken->refresh_token);
        file_put_contents(storage_path('app/client_id.txt'), json_encode($client->getAccessToken()));
    }
1
Grandong

J'utilise google-api-php-client v2.2.2 Je reçois un nouveau jeton avec fetchAccessTokenWithRefreshToken(); si l'appel de fonction ne contient pas de paramètre, il retourne un jeton d'accès mis à jour et le jeton actualisé n'est pas perdu.

if ($client->getAccessToken() && $client->isAccessTokenExpired()) {
    $new_token=$client->fetchAccessTokenWithRefreshToken();
    $token_data = $client->verifyIdToken();
}    
1
Igor Burlov

Google a apporté des modifications depuis la publication de cette question.

Voici mon exemple de travail actuel.

    public function update_token($token){

    try {

        $client = new Google_Client();
        $client->setAccessType("offline"); 
        $client->setAuthConfig(APPPATH . 'vendor' . DIRECTORY_SEPARATOR . 'google' . DIRECTORY_SEPARATOR . 'client_secrets.json');  
        $client->setIncludeGrantedScopes(true); 
        $client->addScope(Google_Service_Calendar::CALENDAR); 
        $client->setAccessToken($token);

        if ($client->isAccessTokenExpired()) {
            $refresh_token = $client->getRefreshToken();
            if(!empty($refresh_token)){
                $client->fetchAccessTokenWithRefreshToken($refresh_token);      
                $token = $client->getAccessToken();
                $token['refresh_token'] = json_decode($refresh_token);
                $token = json_encode($token);
            }
        }

        return $token;

    } catch (Exception $e) { 
        $error = json_decode($e->getMessage());
        if(isset($error->error->message)){
            log_message('error', $error->error->message);
        }
    }


}
0
Dave Spelts

Vous devez enregistrer le jeton d'accès au fichier ou à la base de données sous forme de chaîne json lors de la demande d'autorisation initiale, puis définir le type d'accès sur offline $client->setAccessType("offline").

Ensuite, lors des requêtes suivantes, récupérez le jeton d'accès de votre fichier ou de votre base de données et transmettez-le au client:

$accessToken = json_decode($row['token'], true);
$client->setAccessToken($accessToken);

Maintenant, vous devez vérifier si le jeton a expiré:

if ($client->isAccessTokenExpired()) {
    // access token has expired, use the refresh token to obtain a new one
    $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
    // save the new token to file or db
    // ...json_encode($client->getAccessToken())

La fonction fetchAccessTokenWithRefreshToken() effectuera le travail à votre place et fournira un nouveau jeton d'accès, enregistrez-le dans votre fichier ou votre base de données.

0
Sam Thompson

Selon authentification sur Google: OAuth2 renvoie constamment 'invalid_grant'

"Vous devez réutiliser le jeton d'accès obtenu après la première authentification. Vous obtiendrez une erreur invalid_grant si votre précédent jeton n'a pas encore expiré. Cachez-le quelque part afin de pouvoir le réutiliser."

j'espère que ça aide

0
Jon