Comment rafraîchir token avec Google API client?
j'ai joué avec L'API Google Analytics (V3) et j'ai rencontré des erreurs som. Tout d'abord, tout est mis en place correctement et fonctionne avec mon compte de test. Mais lorsque je veux récupérer des données à partir d'un autre identifiant de profil (même compte Google Accont/GA), j'obtiens une erreur 403. La chose étrange est que les données de certains comptes GA retourneront 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 peux récupérez les données de tous mes comptes. Le problème est résolu? Pas. Comme la clé d'accès expirera, je rencontrerai le même problème à nouveau.
si j'ai bien compris les choses, on pourrait utiliser le resfreshToken pour obtenir une nouvelle authentificationtooken.
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 demande de retour au " apiOAuth2.php fichier". Tous les paramètres sont envoyés correctement. Le type grant_type est difficilement codé en 'refresh_token' dans la méthode, donc il est difficile pour moi de comprendre ce qui ne va pas. Le tableau des 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?
15 réponses
donc j'ai finalement trouvé comment faire ça. L'idée de base est que vous avez le jeton que vous obtenez la première fois que vous demandez pour l'authentification. Ce premier jeton a un jeton de rafraîchissement. Le premier jeton original expire après une heure. Après une heure, vous devez utiliser le jeton d'actualisation de la première gage pour obtenir un nouveau utilisable jeton. Vous utilisez $client->refreshToken($refreshToken)
pour récupérer un nouveau jeton. Je vais appeler cette "temp jeton."Vous devez stocker ce jeton temporaire aussi bien parce qu'après une heure il expire aussi bien et remarque il n'a pas un jeton d'actualisation associé. Pour obtenir un nouveau token temporaire, vous devez utiliser la méthode que vous avez utilisée avant et utiliser le refreshtoken du premier token. J'ai joint le code ci-dessous, ce qui est laid, mais im 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);
Le problème est dans le jeton d'actualisation:
[refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
quand une chaîne avec un '/'
obtient json encoded
, elle est échappée avec un '\'
, donc vous devez l'enlever.
le jeton de rafraîchissement dans votre cas devrait être:
1/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
ce que je suppose que vous avez fait est que vous avez imprimé la chaîne json que google a envoyé et copié et collé le jeton dans votre code parce que si vous json_decode
il alors il supprimera correctement le '\'
pour vous!
Le type d'accès doit être réglé sur offline
. state
est une variable que vous définissez pour votre propre utilisation, et non pour l'utilisation de l'API.
assurez - vous que vous avez la dernière version de la bibliothèque client et ajouter:
$client->setAccessType('offline');
Voir Formant l'URL pour une explication des paramètres.
voici le fragment pour définir le token, avant cela assurez-vous que le type d'accès doit être défini à offline
if (isset($_GET['code'])) {
$client->authenticate();
$_SESSION['access_token'] = $client->getAccessToken();
}
pour rafraîchir le jeton
$google_token= json_decode($_SESSION['access_token']);
$client->refreshToken($google_token->refresh_token);
cela va rafraîchir votre token, vous devez le mettre à jour en session pour que vous puissiez le faire
$_SESSION['access_token']= $client->getAccessToken()
la réponse Postée par @uri-weg a fonctionné pour moi mais comme je n'ai pas trouvé ses explications très claires, permettez-moi de la reformuler un peu.
pendant la première séquence de permission d'accès, dans le rappel, lorsque vous arrivez au point où vous recevez un code d'authentification, vous devez enregistrer le token d'accès et le token de rafraîchissement aussi.
la raison est que l'api google vous envoie un token d'accès avec un actualiser jeton uniquement lors d'une demande d'autorisation d'accès. Les tokens d'accès suivants seront envoyés sans aucun token de rafraîchissement (à moins que vous n'utilisiez l'option approval_prompt=force
).
le jeton de rafraîchissement que vous avez reçu la première fois reste valide jusqu'à ce que l'utilisateur révoque la permission 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 connexion la séquence 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());
}
Voici le code que j'utilise dans mon projet et il fonctionne très 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;
}
avait le même problème; mon script qui a fonctionné hier, pour une raison étrange ne l'a pas fait aujourd'hui. Pas de changements.
apparemment, c'était parce que mon horloge système était éteinte de 2,5 (!!) secondes, la synchronisation avec NTP fixe.
Voir aussi: https://code.google.com/p/google-api-php-client/wiki/OAuth2#Solving_invalid_grant_errors
POUR INFO: L'API Google Analytics 3.0 rafraîchira automatiquement le token d'accès si vous avez un token de rafraîchissement à la date d'expiration de sorte que votre script n'a jamais besoin de refreshToken
.
(voir la fonction Sign
dans auth/apiOAuth2.php
)
j'ai utilisé l'exemple de smartcodes avec la version actuelle de L'API Google, mais celle-ci n'a pas fonctionné. Je pense que son API est trop périmée.
donc, je viens d'écrire ma propre version, basée sur l'un des exemples API... Il affiche le jeton d'accès, le jeton de requête, le type de jeton, le jeton D'ID, le temps d'expiration et le temps de création comme chaînes
si vos justificatifs d'identité de client et votre clé de développeur sont corrects, ce code devrait fonctionner hors de la boîte.
<?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>
rafraîchit parfois le jeton que je n'ai pas généré en utilisant $client->setAccessType ("offline");
.
essayez ceci:
$client->setAccessType ("offline");
$client->setApprovalPrompt ("force");
j'ai le même problème avec google/google-api-php-client v2.0.151930920" et après avoir cherché pendant 1 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()));
}
ceci ici fonctionne très bien, peut-être qu'il pourrait aider n'importe qui:
de l'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();
?>
du 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();
}
?>
selon authentification sur google: OAuth2 continue à retourner "invalid_grant"
" vous devez réutiliser le jeton d'accès que vous obtenez après la première authentification réussie. Vous obtiendrez une erreur invalid_grant si votre jeton précédent n'est pas encore expiré. Cache quelque part afin de la réutiliser."
j'espère que ça aide
Google a fait quelques changements depuis que cette question a été initialement posté.
Voici mon exemple 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);
}
}
}
utilisez l'extrait de code suivant pour obtenir votre jeton de rafraîchissement
<?php
require_once 'src/apiClient.php';
require_once 'src/contrib/apiTasksService.php';
$client = new apiClient();
$client->setAccessType('offline');
$tasksService = new apiTasksService($client);
$auth = $client->authenticate();
$token = $client->getAccessToken();
// the refresh token
$refresh_token = $token['refresh_token'];
?>