php - funciona - ¿Cómo actualizar el token con el cliente de la API de Google?
oauth2 access token url (14)
Algunas veces Refresh Token no se genera usando $client->setAccessType ("offline");
.
Prueba esto:
$client->setAccessType ("offline");
$client->setApprovalPrompt ("force");
He estado jugando con la API de Google Analytics (V3) y me he encontrado con algunos errores. En primer lugar, todo está configurado correctamente y funcionó con mi cuenta de prueba. Pero cuando quiero tomar datos de otra identificación de perfil (Mis cuentas de Google Accont / GA) obtengo un error 403. Lo extraño es que los datos de algunas cuentas de GA devolverán datos, mientras que otros generarán este error.
He revocado el token y autenticado una vez más, y ahora parece que puedo tomar datos de todas mis cuentas. ¿Problema resuelto? No. Como la clave de acceso caducará, me encontraré con el mismo problema nuevamente.
Si he entendido bien las cosas, se podría usar el resfreshToken para obtener una nueva autenticaciónTooken.
El problema es que cuando corro:
$client->refreshToken(refresh_token_key)
se devuelve el siguiente error:
Error refreshing the OAuth2 token, message: ''{ "error" : "invalid_grant" }''
Revisé el código detrás del método refreshToken y rastreé la solicitud al archivo "apiOAuth2.php". Todos los parámetros se envían correctamente. Grant_type está codificado para ''refresh_token'' dentro del método, por lo que es difícil para mí entender qué sucede. La matriz de parámetros se ve así:
Array ( [client_id] => *******-uqgau8uo1l96bd09eurdub26c9ftr2io.apps.googleusercontent.com [client_secret] => ******** [refresh_token] => 1//lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY [grant_type] => refresh_token )
El procedimiento es el siguiente.
$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
¿Es esto un error, o he entendido mal algo por completo?
Aquí está el código que estoy usando en mi proyecto y está funcionando 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;
}
Así que finalmente descubrí cómo hacer esto. La idea básica es que tienes el token que obtienes la primera vez que pides autenticación. Este primer token tiene un token de actualización. El primer token original caduca después de una hora. Después de una hora, debe usar el token de actualización del primer token para obtener un nuevo token útil. Utiliza $client->refreshToken($refreshToken)
para recuperar un nuevo token. Llamaré a esto "ficha de temperatura". También debe almacenar este token de temperatura porque después de una hora también caduca y tenga en cuenta que no tiene un token de actualización asociado. Para obtener un nuevo token de temp, debes usar el método que usaste antes y usar el primer token de refreshtoken. He adjuntado el código a continuación, que es feo, pero soy nuevo en esto ...
//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);
De acuerdo con la Autenticación en google: OAuth2 sigue respondiendo ''invalid_grant''
"Debería reutilizar el token de acceso que obtiene después de la primera autenticación exitosa. Obtendrá un error invalid_grant si su token anterior aún no ha caducado. Almacénelo en algún lugar para poder reutilizarlo".
Espero eso ayude
El problema está en el token de actualización:
[refresh_token] => 1//lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
Cuando una cadena con un ''/''
se json encoded
, se escapa con un ''/'
, por lo tanto, debe eliminarlo.
El token de actualización en tu caso debe ser:
1/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
Lo que supongo que has hecho es que has impreso la cadena json que google envió y copió y pegó el token en tu código porque si json_decode
entonces eliminará correctamente el ''/'
por ti.
El tipo de acceso debe establecerse en offline
de offline
. state
es una variable que configura para su propio uso, no el uso de la API.
Asegúrese de tener la última versión de la biblioteca del cliente y agregue:
$client->setAccessType(''offline'');
Consulte Cómo formar la URL para obtener una explicación de los parámetros.
Esto aquí funciona muy bien, tal vez podría ayudar a cualquiera:
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();
}
?>
FYI: La API 3.0 de Google Analytics actualizará automáticamente el token de acceso si tiene un token de actualización cuando caduque, por lo que su script nunca necesita refreshToken
.
(Consulte la función Sign
en auth/apiOAuth2.php
)
Tenía el mismo problema; mi guión que funcionó ayer, por alguna extraña razón, hoy no. Sin cambios.
Aparentemente esto fue porque mi reloj del sistema estaba apagado por 2.5 (!!) segundos, sincronizándolo con NTP.
Ver también: https://code.google.com/p/google-api-php-client/wiki/OAuth2#Solving_invalid_grant_errors
Tengo el mismo problema con google / google-api-php-client v2.0.0-RC7 y después de buscar durante 1 hora, resolví este problema usando json_encode de esta manera:
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()));
}
Usé el ejemplo mediante códigos inteligentes con la versión actual de la API de Google, pero ese no funcionó. Creo que su API es demasiado obsoleta.
Por lo tanto, acabo de escribir mi propia versión, basada en uno de los ejemplos de la API ... Muestra token de acceso, token de solicitud, tipo de token, token de ID, tiempo de expiración y tiempo de creación como cadenas
Si las credenciales de su cliente y la clave del desarrollador son correctas, este código debería funcionar de la caja.
<?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>
aquí está el fragmento para configurar el token, antes de eso, asegúrese de que el tipo de acceso esté configurado como desconectado
if (isset($_GET[''code''])) {
$client->authenticate();
$_SESSION[''access_token''] = $client->getAccessToken();
}
Para actualizar el token
$google_token= json_decode($_SESSION[''access_token'']);
$client->refreshToken($google_token->refresh_token);
esto actualizará tu token, tienes que actualizarlo en sesión para que puedas hacer
$_SESSION[''access_token'']= $client->getAccessToken()
usa el siguiente fragmento de código para obtener tu token de actualización
<?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''];
?>
La respuesta publicada por @uri-weg funcionó para mí, pero como no encontré sus explicaciones muy claras, permítanme reformularla un poco.
Durante la primera secuencia de permisos de acceso, en la devolución de llamada, cuando llega al punto donde recibe un código de autenticación, debe guardar el token de acceso y el token de actualización también.
El motivo es que google api le envía un token de acceso con un token de actualización solo cuando solicita permiso de acceso. Los siguientes tokens de acceso se enviarán sin ningún token de actualización (a menos que use la opción approval_prompt=force
).
El token de actualización que recibió la primera vez permanece válido hasta que el usuario revoca el permiso de acceso.
En php simplista, un ejemplo de la secuencia de devolución de llamada sería:
// 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'']);
Y más adelante, en php simplista, la secuencia de conexión sería:
// 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());
}