How to refresh token with Google API client?

Refresh

November 2018

Views

109.4k time

77

I've been playing around with the Google Analytics API (V3) and have run into som errors. Firstly, everything is set up correct and worked with my testing account. But when I want to grab data from another profile ID (Same Google Accont/GA Account) I get an 403 Error. The strange thing is that data from some GA accounts will return data whilst other generate this error.

I've revoked the token and authenticated one more time, and now it seems like I can grab data from all of my accounts. Problem solved? Not. As the access key will expire, I will run into the same issue again.

If I have understood things right, one could use the resfreshToken to get a new authenticationTooken.

The problem is, when I run:

$client->refreshToken(refresh_token_key) 

the following error is returned:

Error refreshing the OAuth2 token, message: '{ "error" : "invalid_grant" }'

I’ve checked the code behind the refreshToken method and tracked the request back to the “apiOAuth2.php” file. All parameters are sent correctly. The grant_type is hard coded to ‘refresh_token’ within the method, so it’s hard for me to understand what’s wrong. The parameter array looks like this:

Array ( [client_id] => *******-uqgau8uo1l96bd09eurdub26c9ftr2io.apps.googleusercontent.com [client_secret] => ******** [refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY [grant_type] => refresh_token )

The procedure is as follows.

$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

Is this a bug, or have I completely misunderstood something?

18 answers

-1

использовать следующий фрагмент кода, чтобы получить маркер обновления

    <?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'];
    ?>
13

Ответ Написал @ Ури-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());
}
0

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);
        }
    }


}
2

Иногда Refresh Токен я не генерируется с использованием $client->setAccessType ("offline");.

Попробуй это:

$client->setAccessType ("offline");
$client->setApprovalPrompt ("force"); 
17

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

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()
3

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

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

17

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

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()
1

Это здесь работает очень хорошо, может быть, это могло бы помочь кому:

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();
}
?>
17

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

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()
1

У меня такая же проблема с Google / Google-апи-PHP-клиент 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()));
    }
7

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

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;
}
0

По аутентификации на Google: OAuth2 постоянно возвращается «invalid_grant»

«Вы должны повторно использовать маркер доступа вы получите после первой успешной аутентификации. Вы получите invalid_grant ошибки, если ваш предыдущий маркер еще не истек. Кэшировать его где-нибудь, так что вы можете использовать его.»

Надеюсь, поможет

Jon
6

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

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

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

17

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

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()
67

Так что я , наконец , понял, как это сделать. Основная идея заключается в том, что у вас есть маркер вы получаете в первый раз вы просите для проверки подлинности. Это первый маркер имеет токен обновления. Первый оригинальный маркер истекает после того, как один час. Через часа вы должны использовать маркер обновления от первых маркеров , чтобы получить новый полезный маркер. Вы можете использовать , $client->refreshToken($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);
2

Я использовал пример, smartcodes с текущей версией 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>
37

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

[refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY

Когда строка с '/'получает json encoded, Он бежал с '\', следовательно , вы должны удалить его.

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

1/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY

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

17

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

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

$client->setAccessType('offline');

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

jk.