Я играл с Google Analytics API (V3) и сталкивался с ошибками. Во-первых, все настроено правильно и работает с моей тестовой учетной записью. Но когда я хочу получить данные из другого идентификатора профиля (та же учетная запись Google Accont / GA), я получаю ошибку 403. Странно то, что данные из некоторых учетных записей GA будут возвращать данные, в то время как другие генерируют эту ошибку.
Я отозвал токен и прошел аутентификацию еще раз, и теперь мне кажется, что я могу получать данные со всех своих учетных записей. Задача решена? Не. Поскольку срок действия ключа доступа истечет, я снова столкнусь с той же проблемой.
Если я правильно понял, можно было бы использовать resfreshToken для получения нового authenticationTooken.
Проблема в том, что когда я бегу:
$client->refreshToken(refresh_token_key)
возвращается следующая ошибка:
Error refreshing the OAuth2 token, message: '{ "error" : "invalid_grant" }'
Я проверил код метода 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)
для получения нового токена. Я назову это «временным токеном». Вам также необходимо сохранить этот временный токен, потому что через час он также истекает, и обратите внимание, что с ним не связан токен обновления. Чтобы получить новый временный токен, вам необходимо использовать метод, который вы использовали раньше, и использовать первый токен обновления. Я прикрепил код ниже, который уродлив, но я новичок в этом ...//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);
источник
$client->isAccessTokenExpired()
прежнему будет проверяться только время, удерживаемое локально, чтобы узнать, считает ли он, что срок действия токена истек. Возможно, срок действия токена все еще истек, и локальное приложение действительно узнает об этом только тогда, когда попытается его использовать. В этом случае клиент API вернет исключение и не будет автоматически обновлять токен.Проблема в токене обновления:
[refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
Когда строка с a
'/'
получаетjson encoded
, она экранируется с помощью a'\'
, поэтому вам необходимо удалить ее.Токен обновления в вашем случае должен быть:
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();
) для использования токена обновления?$client->setApprovalPrompt('force')
а также$client->setAccessType('offline')
получить новый токен обновления при авторизации. Не заставляя пользователя утверждать объем доступа, Google предполагает, что вы собираетесь продолжать использовать старый токен обновления.Ответ, опубликованный @ 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; }
источник
Была такая же проблема; мой сценарий, который работал вчера, по какой-то странной причине не работал сегодня. Без изменений.
По-видимому, это было из-за того, что мои системные часы были отключены на 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. Он синхронизировал часы, и проблема была решена.К вашему сведению: 3.0 Google Analytics API автоматически обновит токен доступа, если у вас будет токен обновления, когда он истечет, поэтому вашему скрипту никогда не понадобится
refreshToken
.(См.
Sign
Функцию вauth/apiOAuth2.php
)источник
Иногда токен обновления не создается при использовании
$client->setAccessType ("offline");
.Попробуй это:
$client->setAccessType ("offline"); $client->setApprovalPrompt ("force");
источник
Я использовал пример смарт-кодов с текущей версией Google API, но эта не сработала. Я считаю его 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'];
. Почему вы перенаправляете на ту же страницу? это необходимо?Вам необходимо сохранить токен доступа в файл или базу данных в виде строки json во время первоначального запроса авторизации и установить для типа доступа значение offline.
$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 / 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())); }
источник
Здесь это работает очень хорошо, может быть, это кому-нибудь поможет:
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 внес некоторые изменения с момента первоначальной публикации этого вопроса.
Вот мой рабочий пример.
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-api-php-client v2.2.2. Я получаю новый токен с
fetchAccessTokenWithRefreshToken();
вызовом функции if без параметров, он возвращает обновленный токен доступа, и обновленный токен не теряется.if ($client->getAccessToken() && $client->isAccessTokenExpired()) { $new_token=$client->fetchAccessTokenWithRefreshToken(); $token_data = $client->verifyIdToken(); }
источник
используйте следующий фрагмент кода, чтобы получить токен обновления
<?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']; ?>
источник
Согласно аутентификации в google: OAuth2 продолжает возвращать invalid_grant
«Вам следует повторно использовать токен доступа, который вы получите после первой успешной аутентификации. Вы получите ошибку invalid_grant, если срок действия вашего предыдущего токена еще не истек. Сохраните его где-нибудь, чтобы вы могли повторно использовать».
Надеюсь, это поможет
источник