Я играл с API Google Analytics (V3) и сталкивался с ошибками som. Во-первых, все настроено правильно и работает с моей тестовой учетной записью. Но когда я хочу захватить данные из другого идентификатора профиля (тот же аккаунт Google Accont/GA), я получаю ошибку 403. Странно, что данные из некоторых учетных записей GA будут возвращать данные, в то время как другие генерируют эту ошибку.
Я отозвал токен и прошел проверку подлинности еще раз, и теперь кажется, что я могу захватить данные со всех моих учетных записей. Задача решена? Не. По мере того, как ключ доступа истечет, я снова столкнусь с тем же вопросом.
Если я понял все правильно, можно было бы использовать resfreshToken для получения новой аутентификацииTooken.
Проблема заключается в том, что я запускаю:
$client->refreshToken(refresh_token_key)
возвращается следующая ошибка:
Error refreshing the OAuth2 token, message: '{ "error" : "invalid_grant" }'
Ive проверил код за методом refreshToken и отправил запрос обратно в файл apiOAuth2.php. Все параметры отправляются правильно. Тип grant_type жестко закодирован до 'refresh_token внутри метода, поэтому мне трудно понять, что не так. Массив параметров выглядит следующим образом:
Array ( [client_id] => *******-uqgau8uo1l96bd09eurdub26c9ftr2io.apps.googleusercontent.com [client_secret] => ******** [refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY [grant_type] => refresh_token )
Процедура следующая.
$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
Является ли это ошибкой, или я совершенно ничего не понял?
Итак, я наконец понял, как это сделать. Основная идея заключается в том, что у вас есть маркер, который вы получаете при первом запросе аутентификации. Этот первый токен имеет токен обновления. Первый оригинальный токен истекает через час. Через час вам нужно использовать токен обновления с первого токена, чтобы получить новый полезный токен. Вы используете $client->refreshToken($refreshToken)
для получения нового токена. Я назову этот "токен темпа". Вам нужно также сохранить этот токен темпа, потому что через час он истекает, а также отмечает, что у него нет токена обновления, связанного с ним. Чтобы получить новый токен temp, вам нужно использовать метод, который вы использовали ранее, и использовать первый токен refreshtoken. Я приложил код ниже, который является уродливым, но im new в этом...
//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);
Проблема заключается в токере обновления:
[refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
Когда строка с '/'
получает json encoded
, она экранируется с помощью '\'
, поэтому вам нужно ее удалить.
Ток обновления в вашем случае должен быть:
1/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
То, что я предполагаю, что вы сделали, это то, что вы напечатали строку json, которую google отправил обратно и скопировал и вставил токен в ваш код, потому что если вы json_decode
, то он правильно удалит '\'
для вас!
вот фрагмент для установки токена, перед тем как убедиться, что тип доступа должен быть установлен на в автономном режиме
if (isset($_GET['code'])) {
$client->authenticate();
$_SESSION['access_token'] = $client->getAccessToken();
}
Обновить токен
$google_token= json_decode($_SESSION['access_token']);
$client->refreshToken($google_token->refresh_token);
это обновит ваш токен, вам нужно обновить его в сеансе, чтобы вы могли сделать
$_SESSION['access_token']= $client->getAccessToken()
Тип доступа должен быть установлен на offline
. state
- это переменная, которую вы устанавливаете для собственного использования, а не для использования API.
Убедитесь, что у вас есть последняя версия клиентской библиотеки и добавьте:
$client->setAccessType('offline');
См. Формирование URL для объяснения параметров.
$client
( $client = new apiClient();
) для использования токена обновления?
Ответ @uri-weg работал на меня, но, поскольку я не нашел его объяснения очень ясными, позвольте мне немного перефразировать его.
Во время первой последовательности прав доступа, в обратном вызове, когда вы добираетесь до точки, где вы получаете код аутентификации, вы должны также сохранить токен доступа и токен обновления.
Причина в том, что Google API отправляет вам токен доступа с токеном обновления только при запросе разрешения на доступ. Следующие токены доступа будут отправлены без каких-либо токенов обновления (если вы не используете опцию approval_prompt=force
).
Маркер обновления, полученный вами в первый раз, остается действительным до тех пор, пока пользователь не отменит разрешение на доступ.
В упрощенном php примером последовательности обратного вызова будет:
// 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']);
И позже, в упрощенном php, последовательность подключения будет такой:
// 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());
}
// setAccessToken() expects json
было достаточно. Или это для другой части кода?
Вот код, который я использую в своем проекте, и он работает нормально:
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;
}
Была та же проблема; мой script, который работал вчера, по какой-то нечетной причине не сегодня. Без изменений.
По-видимому, это связано с тем, что мои системные часы были отключены на 2,5 (!!) секунды, синхронизация с NTP фиксировала его.
Смотрите также: https://code.google.com/p/google-api-php-client/wiki/OAuth2#Solving_invalid_grant_errors
sudo apt-get install ntp
на своем компьютере Debian, чтобы установить NTP. Он синхронизировал часы и проблема была решена.
Иногда обновлять токен я не генерируется с помощью $client->setAccessType ("offline");
.
Попробуйте следующее:
$client->setAccessType ("offline");
$client->setApprovalPrompt ("force");
$client->setApprovalPrompt ("force");
это 100% работает ...
FYI: 3.0 API Google Analytics автоматически обновит токен доступа, если у вас есть токен обновления, когда он истечет, поэтому ваш script никогда не нуждается в refreshToken
.
(См. функцию Sign
в auth/apiOAuth2.php
)
Я использовал пример с помощью смарткодов с текущей версией API Google, но этот не работал. Я думаю, что его API слишком устарел.
Итак, я просто написал свою собственную версию на основе одного из примеров API... Он выводит токен доступа, маркер запроса, токен, токен идентификатора, время истечения и время создания в виде строк
Если ваши учетные данные и ключ вашего клиента верны, этот код должен работать из коробки.
<?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>
$redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
, Почему вы перенаправляете на ту же страницу? это необходимо?
Я использую google-api-php-client v2.2.2. Я получаю новый токен с fetchAccessTokenWithRefreshToken();
если вызов функции без параметров, он возвращает обновленный токен доступа и обновленный токен не теряется.
if ($client->getAccessToken() && $client->isAccessTokenExpired()) {
$new_token=$client->fetchAccessTokenWithRefreshToken();
$token_data = $client->verifyIdToken();
}
Это работает очень хорошо, может быть, это может помочь любому:
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();
}
?>
У меня такая же проблема с google/google-api-php-client v2.0.0-RC7 и после поиска в течение 1 часа я решил эту проблему, используя json_encode например:
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()));
}
Во время первоначального запроса авторизации необходимо сохранить токен доступа в файл или базу данных в виде строки json и установить тип доступа в автономный режим $client->setAccessType("offline")
Затем, во время последующих запросов API, извлеките токен доступа из вашего файла или базы данных и передайте его клиенту:
$accessToken = json_decode($row['token'], true);
$client->setAccessToken($accessToken);
Теперь вам нужно проверить, не истек ли токен:
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())
Функция fetchAccessTokenWithRefreshToken()
сделает всю работу за вас и предоставит новый токен доступа, сохранит его обратно в файл или базу данных.
Google внес некоторые изменения с тех пор, как этот вопрос был впервые опубликован.
Вот мой в настоящее время рабочий пример.
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);
}
}
}
Согласно Аутентификация в google: OAuth2 продолжает возвращать 'invalid_grant'
"Вы должны повторно использовать токен доступа, который вы получите после первой успешной проверки подлинности. Вы получите ошибку invalid_grant, если ваш предыдущий токен еще не истек. Загрузите его где-нибудь, чтобы вы могли его повторно использовать".
надеюсь, что это поможет
используйте следующий фрагмент кода, чтобы получить токен обновления
<?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'];
?>