Как обновить токен с клиентом Google API? - PullRequest
85 голосов
/ 11 февраля 2012

Я играл с Google Analytics API (V3) и столкнулся с ошибками сома.Во-первых, все настроено правильно и работает с моей тестовой учетной записью.Но когда я хочу получить данные из другого идентификатора профиля (та же учетная запись Google Accont / GA), я получаю сообщение об ошибке 403.Странно то, что данные из некоторых учетных записей GA будут возвращать данные, в то время как другие генерируют эту ошибку.

Я отозвал токен и снова аутентифицировался, и теперь мне кажется, что я могу получить данные со всех своихСчета.Задача решена?Не.Поскольку срок действия ключа доступа истекает, я снова столкнусь с той же проблемой.

Если я все правильно понял, можно использовать resfreshToken для получения нового аутентификационного ключа.

Проблема в том,когда я запускаю:

$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

Это ошибка или я что-то не так понял?

Ответы [ 17 ]

74 голосов
/ 13 марта 2013

Итак, я наконец-то понял, как это сделать. Основная идея заключается в том, что у вас есть токен, который вы получаете при первом запросе аутентификации. Этот первый токен имеет токен обновления. Первый оригинальный токен истекает через час. Через час вы должны использовать токен обновления с первого токена, чтобы получить новый используемый токен. Вы используете $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);
41 голосов
/ 20 января 2013

Проблема в маркере обновления:

[refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY

Когда строка с '/' получает json encoded, она экранируется с '\', поэтому вам необходимо удалить ее.

Токен обновления в вашем случае должен быть:

1/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY

Я предполагаю, что вы сделали, что вы напечатали строку json, которую Google отправил обратно, скопировал и вставилтокен в вашем коде, потому что если вы json_decode, то он будет правильно удалить '\' для вас!

18 голосов
/ 26 февраля 2013

здесь приведен фрагмент для установки токена, перед этим убедитесь, что тип доступа установлен на offline

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()
17 голосов
/ 12 февраля 2012

Тип доступа должен быть установлен на offline.state - это переменная, которую вы устанавливаете для своего собственного использования, а не для использования API.

Убедитесь, что у вас установлена ​​ последняя версия клиентской библиотеки и добавьте:

$client->setAccessType('offline');

См. Формирование URL для объяснения параметров.

14 голосов
/ 19 июля 2016

Ответ, отправленный @ 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());
}
7 голосов
/ 03 августа 2016

Вот код, который я использую в своем проекте, и он работает нормально:

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;
}
6 голосов
/ 23 августа 2012

была такая же проблема; мой сценарий, который работал вчера, по какой-то странной причине не сегодня. Без изменений.

Видимо, это было потому, что мои системные часы были выключены на 2,5 (!!) секунды, синхронизация с NTP исправила это.

Смотри также: https://code.google.com/p/google-api-php-client/wiki/OAuth2#Solving_invalid_grant_errors

3 голосов
/ 14 сентября 2012

FYI: API Google Analytics 3.0 автоматически обновит токен доступа, если у вас есть токен обновления, когда он истекает, поэтому вашему скрипту никогда не требуется refreshToken.

(См. Функцию Sign в auth/apiOAuth2.php)

3 голосов
/ 14 февраля 2017

Иногда обновить маркер, который не генерируется с помощью $client->setAccessType ("offline");.

Попробуйте:

$client->setAccessType ("offline");
$client->setApprovalPrompt ("force"); 
2 голосов
/ 15 ноября 2013

Я использовал пример по смарт-кодам с текущей версией 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...