it-roy-ru.com

Google API Client "маркер обновления должен быть передан или задан как часть setAccessToken"

В настоящее время я сталкиваюсь с очень странной проблемой, действительно я следую этому же руководству ( https://developers.google.com/google-apps/calendar/quickstart/php ) из документации по API Google. Я попробовал это дважды, в первый раз он работал как чудо, но после истечения срока действия токена скрипт, предоставленный Google API, не смог обновить его.

TL; DR

Вот сообщение об ошибке:

[email protected]:~$ php www/path/to/app/public/quickstart.php


Fatal error: Uncaught exception 'LogicException' with message 'refresh token must be passed in or set as part of setAccessToken' in /home/pueblo/www/path/to/app/vendor/google/apiclient/src/Google/Client.php:258
Stack trace:
#0 /home/pueblo/www/path/to/app/public/quickstart.php(55): Google_Client->fetchAccessTokenWithRefreshToken(NULL)
#1 /home/pueblo/www/path/to/app/public/quickstart.php(76): getClient()
#2 {main}
  thrown in /home/pueblo/www/path/to/app/vendor/google/apiclient/src/Google/Client.php on line 258

Вот часть php-скрипта от Google, которую я модифицировал:

require_once __DIR__ . '/../vendor/autoload.php';

// I don't want the creds to be in my home folder, I prefer them in the app's root
define('APPLICATION_NAME', 'LRS API Calendar');
define('CREDENTIALS_PATH', __DIR__ . '/../.credentials/calendar-php-quickstart.json');
define('CLIENT_SECRET_PATH', __DIR__ . '/../client_secret.json');

Я также изменил expandHomeDirectory, чтобы я мог «отключить» его, не изменяя слишком много кода:

function expandHomeDirectory($path) {
  $homeDirectory = getenv('HOME');
  if (empty($homeDirectory)) {
    $homeDirectory = getenv('HOMEDRIVE') . getenv('HOMEPATH');
  }
  return $path;
  // return str_replace('~', realpath($homeDirectory), $path);
}

Поэтому, чтобы проверить, был ли я неправ или Google был, я провел эксперимент: вчера вечером я запустил скрипт быстрого запуска из ssh, чтобы проверить, работает ли он, и действительно, поэтому я решил проверить сегодня утром, работает ли он просто как это было до того, как я спал, и это не так, я думаю, что-то не так с quickstart.php от Google.

Я надеюсь, что кто-то может мне помочь, я уже проверил все другие сообщения на эту тему, но они все устарели.

12
Sam

Недавно я получил ту же проблему, и я решил ее с этим.

<?php
 $client->setRedirectUri($this->_redirectURI);
 $client->setAccessType('offline');
 $client->setApprovalPrompt('force');

Я объясняю ..... Жетон обновления не возвращается, потому что мы не форсировали утверждение. Автономный режим не достаточно. Мы должны форсировать утверждение. Также redirectURI должен быть установлен перед этими двумя опциями. Это сработало для меня.

Это моя полная функция

<?php
     private function getClient()
     {
        $client = new Google_Client();
        $client->setApplicationName($this->projectName);
        $client->setScopes(SCOPES);
        $client->setAuthConfig($this->jsonKeyFilePath);
        $client->setRedirectUri($this->redirectUri);
        $client->setAccessType('offline');
        $client->setApprovalPrompt('force');

       // Load previously authorized credentials from a file.
       if (file_exists($this->tokenFile)) {
         $accessToken = json_decode(file_get_contents($this->tokenFile), 
         true);
      } else {
        // Request authorization from the user.
        $authUrl = $client->createAuthUrl();
        header('Location: ' . filter_var($authUrl, FILTER_SANITIZE_URL));

        if (isset($_GET['code'])) {
            $authCode = $_GET['code'];
            // Exchange authorization code for an access token.
            $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
            header('Location: ' . filter_var($this->redirectUri, 
            FILTER_SANITIZE_URL));
            if(!file_exists(dirname($this->tokenFile))) {
                mkdir(dirname($this->tokenFile), 0700, true);
            }

            file_put_contents($this->tokenFile, json_encode($accessToken));
        }else{
            exit('No code found');
        }
    }
    $client->setAccessToken($accessToken);

    // Refresh the token if it's expired.
    if ($client->isAccessTokenExpired()) {

        // save refresh token to some variable
        $refreshTokenSaved = $client->getRefreshToken();

        // update access token
        $client->fetchAccessTokenWithRefreshToken($refreshTokenSaved);

        // pass access token to some variable
        $accessTokenUpdated = $client->getAccessToken();

        // append refresh token
        $accessTokenUpdated['refresh_token'] = $refreshTokenSaved;

        //Set the new acces token
        $accessToken = $refreshTokenSaved;
        $client->setAccessToken($accessToken);

        // save to file
        file_put_contents($this->tokenFile, 
       json_encode($accessTokenUpdated));
    }
    return $client;
}
17
Ulrich Dohou

Я столкнулся с той же проблемой с новой библиотекой Google API. При поиске решения появилась следующая ссылка: RefreshToken Не получаю отправку обратно после того, как я получу новый токен API листов Google

Основываясь на этой информации, я изменил часть кода быстрого запуска в соответствии со своими потребностями. После первой авторизации в Google я получил drive-php-quickstart.json, который содержит файл refresh_token, срок действия которого истекает через 3600 секунд или один час. Токен обновления выдается только один раз, поэтому, если он утерян, требуется повторная авторизация .. Итак, чтобы он всегда был в drive-php-quickstart.json, я сделал следующее:

// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
// save refresh token to some variable
$refreshTokenSaved = $client->getRefreshToken(); 

// update access token
$client->fetchAccessTokenWithRefreshToken($refreshTokenSaved); 

// pass access token to some variable
$accessTokenUpdated = $client->getAccessToken();

// append refresh token
$accessTokenUpdated['refresh_token'] = $refreshTokenSaved;

// save to file
file_put_contents($credentialsPath, json_encode($accessTokenUpdated)); 
}
4
Salavat Sayfullin

Мой совет: сохраняйте токен обновления в .json сразу после получения токена доступа , а если срок действия токена истек, используйте токен обновления.

В моих проектах работают так:

public static function getClient()
{
    $client = new Google_Client();
    $client->setApplicationName('JhvInformationTable');
    $client->setScopes(Google_Service_Calendar::CALENDAR_READONLY);
    $client->setAuthConfig('credentials.json');
    $client->setAccessType('offline');

    // Load previously authorized credentials from a file.
    $credentialsPath = 'token.json';
    $credentialsPath2 = 'refreshToken.json';
    if (file_exists($credentialsPath)) {
        $accessToken = json_decode(file_get_contents($credentialsPath), true);
    } else {
        // Request authorization from the user.
        $authUrl = $client->createAuthUrl();
        //printf("Open the following link in your browser:\n%s\n", $authUrl);
        //print 'Enter verification code: ';
        $authCode = trim(fgets(STDIN));

        //echo "<script> location.href='".$authUrl."'; </script>";
        //exit;

        $authCode ='********To get code, please uncomment the code above********';

        // Exchange authorization code for an access token.
        $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
        $refreshToken = $client->getRefreshToken();

        // Check to see if there was an error.
        if (array_key_exists('error', $accessToken)) {
            throw new Exception(join(', ', $accessToken));
        }

        // Store the credentials to disk.
        if (!file_exists(dirname($credentialsPath))) {
            mkdir(dirname($credentialsPath), 0700, true);
        }
        file_put_contents($credentialsPath, json_encode($accessToken));
        file_put_contents($credentialsPath2, json_encode($refreshToken));
        printf("Credentials saved to %s\n", $credentialsPath);
    }
    $client->setAccessToken($accessToken);

    // Refresh the token if it's expired.
    if ($client->isAccessTokenExpired()) {
        $refreshToken = json_decode(file_get_contents($credentialsPath2), true);
        $client->fetchAccessTokenWithRefreshToken($refreshToken);
        file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
    }
    return $client;
}
3
Michal Blažek

просто какое-то обновление для тех, у кого возникли проблемы с этим сообщением, в основном потому, что только первая команда fetchAccessTokenWithAuthCode () генерирует учетные данные, которые содержат токен обновления (технически действителен вечно - срок действия 2 часа, если вы его не отзываете). Когда вы получаете новый, он заменяет оригинальный, но не содержит токен обновления, в котором он нуждается, поэтому в следующий раз, когда вам потребуется обновить токен, он выйдет из строя. Это можно легко исправить, заменив функцию обновления следующим образом:

  // Refresh the token if it's expired.
  if ($client->isAccessTokenExpired()) {
    $oldAccessToken=$client->getAccessToken();
    $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
    $accessToken=$client->getAccessToken();
    $accessToken['refresh_token']=$oldAccessToken['refresh_token'];
    file_put_contents($credentialsPath, json_encode($accessToken));
}

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

2
Therian

У меня были те же проблемы, и, наконец, приступить к работе:

Предыстория:

Я получил ту же ошибку. Вот что я нашел:

Эта ошибка:

Неустранимая ошибка PHP: Uncaught LogicException: токен обновления должен быть передан или установлен как часть setAccessToken in /Library/WebServer/Documents/Sites/test/scripts/vendor/google/apiclient/src/Google/Client.php : 267

Ссылка на метод обновления маркера доступа (он же Refresh):

$client->fetchAccessTokenWithRefreshToken($refreshTokenSaved);

Почему это не удалось? Короче говоря, я понял, когда распечатал массив $ accessToken, который получается при декодировании этого файла json (в соответствии с кодом быстрого запуска, который вы разместили/который исходит от Google)

учетные данные/календарь-PHP-quickstart.json

Я обнаружил ошибку из-за того, как распечатывается массив accessToken, когда я печатаю:

Массив ( [access_token] => Массив ( [Access_token] => xyz123 [Token_type] => Носитель [Expires_in] => 3600 [Refresh_token] => xsss112222 [Создано] => 1511379484 )

)

Решение:

$ refreshToken = $ accessToken ["access_token"] ["refresh_token"];

прямо перед этой строкой:

    $client->fetchAccessTokenWithRefreshToken($refreshToken);

Я могу, наконец, обновить токен по мере необходимости, когда он истекает через час. Я думаю, что разработчики этой статьи предполагали, что распечатки массива будут выглядеть так:

Массив ( [access_token] => xyz123 [token_type] => Носитель [expires_in] => 3600 [refresh_token] => xsss112222 [создан] => 1511379484 )

поэтому они подумали, что вы можете просто сделать $ accessToken ["refresh_token"]; Это не правильно. 

Теперь у нас есть правильное значение для $ refreshToken, поэтому ошибка должна исчезнуть, если вы сделаете это. Я также обновил автора через ссылку обратной связи, чтобы сообщить им об этом на случай, если другие разработчики php столкнутся с этой проблемой. Надеюсь, это кому-нибудь поможет. Мои извинения, если я плохо отформатировал этот пост, я новичок в S.E. Я просто хотел поделиться, как я наконец получил это на работу.

1
user3760763

Вам необходимо сериализовать accestoken, когда вы записываете его в credentialsPath.

 // Exchange authorization code for an access token.
    $accessToken = $client->authenticate($authCode);

    // Store the credentials to disk.
    if(!file_exists(dirname($credentialsPath))) {
        mkdir(dirname($credentialsPath), 0700, true);
    }
    $serArray = serialize($accessToken);
    file_put_contents($credentialsPath, $serArray);
    printf("Credentials saved to %s\n", $credentialsPath);

А когда вы читаете из файла, вам нужно его не сериализовать.

if (file_exists($credentialsPath)) {
    $unserArray =  file_get_contents($credentialsPath);
    $accessToken = unserialize($unserArray);

}

Полная функция

function getClient() {
    $client = new Google_Client();
    // Set to name/location of your client_secrets.json file.
    $client->setAuthConfigFile('client_secret.json');
    // Set to valid redirect URI for your project.
    $client->setRedirectUri('http://localhost');
    $client->setApprovalPrompt('force');

    $client->addScope(Google_Service_YouTube::YOUTUBE_READONLY);
    $client->setAccessType('offline');

    // Load previously authorized credentials from a file.
    $credentialsPath = expandHomeDirectory(CREDENTIALS_PATH);


    if (file_exists($credentialsPath)) {
        $unserArray =  file_get_contents($credentialsPath);
        $accessToken = unserialize($unserArray);

    } else {
        // Request authorization from the user.
        $authUrl = $client->createAuthUrl();
        printf("Open the following link in your browser:\n%s\n", $authUrl);
        print 'Enter verification code: ';
        $authCode = trim(fgets(STDIN));

        // Exchange authorization code for an access token.
        $accessToken = $client->authenticate($authCode);

        // Store the credentials to disk.
        if(!file_exists(dirname($credentialsPath))) {
            mkdir(dirname($credentialsPath), 0700, true);
        }
        $serArray = serialize($accessToken);
        file_put_contents($credentialsPath, $serArray);
        printf("Credentials saved to %s\n", $credentialsPath);
    }

    $client->setAccessToken($accessToken);

    // Refresh the token if it's expired.
    if ($client->isAccessTokenExpired()) {
        $client->refreshToken($client->getRefreshToken());
        file_put_contents($credentialsPath, $client->getAccessToken());
    }
    return $client;
}
1
Nick de Pannekoek

Итак, через некоторое время просмотра этого кода:

// Exchange authorization code for an access token.
// "refresh_token" is returned along with the access token
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);


// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
    $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
    file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}

все, что нужно было, это изменение:

// Exchange authorization code for an access token.
// "refresh_token" is returned along with the access token
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);


// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
    $client->fetchAccessTokenWithRefreshToken($accessToken);
    file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}

так как эта функция $ client-> getRefreshToken () возвращает значение null, и если вы предоставите $ accessToken напрямую, она будет работать нормально и обновит ваш файл, надеюсь, что это решит проблему.

0
oron abutbul

Google обновил их PHP Quickstart , улучшив способ для этого:

Фрагмент ниже:

// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);

// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
0
dxdc

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

$client->setAccessType('offline');

Как только это будет сделано, пример кода, указанный в документации Google, будет работать.

// Exchange authorization code for an access token.
// "refresh_token" is returned along with the access token
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);


// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
    $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
    file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
0
jahackbeth