必须将Google API客户端"刷新令牌传入或设置为setAccessToken"

时间:2016-09-04 08:05:47

标签: php google-api google-calendar-api access-token google-api-php-client

我目前面临一个非常奇怪的问题,实际上我一直在使用Google API文档中的相同指南(https://developers.google.com/google-apps/calendar/quickstart/php)。我试了两次,第一次它像魅力一样工作但是在访问令牌过期后,Google API Doc直接提供的脚本无法刷新它。

TL; DR

以下是错误消息:

sam@ssh:~$ 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脚本的一部分:

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启动快速启动脚本来检查它是否正常工作,确实是这样,所以我决定今天早上检查一下我仍在工作,就像我睡觉前一样,而且我认为Google的quickstart.php还有问题。

我希望有人可以帮助我,我已经检查了有关该主题的所有其他帖子,但它们都已过时。

9 个答案:

答案 0 :(得分:17)

我最近遇到了同样的问题,我解决了这个问题。

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

我解释..... 不返回刷新令牌,因为我们没有强制使用approvalPrompt。离线模式不适用。我们必须强制批准提示。还必须在这两个选项之前设置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;
}

答案 1 :(得分:4)

我遇到了与新的谷歌API库相同的问题。搜索解决方案带来以下链接: RefreshToken Not getting send back after I get new token google sheets API

根据这些信息,我修改了快速启动代码部分以满足我的需求。在第一次授权Google之后,我获得了drive-php-quickstart.json,其中包含在3600秒或一小时后到期的refresh_token。刷新令牌只发出一次,因此如果丢失,则需要重新授权。 所以,为了让它始终在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)); 
}

答案 2 :(得分:3)

我的建议是在获取访问令牌后立即将刷新令牌保存到.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 :(得分:2)

对于任何遇到此消息问题的人来说只是一些更新,主要是因为只有第一个命令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));
}

现在,每当您更新访问令牌时,您的刷新令牌也会被传递下去。

答案 4 :(得分:1)

我遇到了同样的问题,最后开始工作:

<强>背景故事:

我收到了同样的错误。这是我发现的:

此错误:

PHP致命错误:未捕获LogicException:必须在/ Library / WebServer / Documents / Sites / test / scripts / vendor / google / apiclient / src中传递或设置刷新令牌作为 setAccessToken 的一部分/Google/Client.php:267

引用更新访问令牌(又名刷新)方法:

$client->fetchAccessTokenWithRefreshToken($refreshTokenSaved);

为什么失败了?长话短说,我意识到当我打印出$ accessToken数组时,它来自解码这个json文件(根据你发布的快速入门代码/来自google)

凭证/日历-PHP-quickstart.json

我发现错误是由于在print_r:

时accessToken数组的打印方式

<强>阵列 (      [access_token] =&gt;阵列         (             [access_token] =&gt; XYZ123             [token_type] =&gt;来人             [expires_in] =&gt; 3600             [refresh_token] =&gt; xsss112222             [created] =&gt; 1511379484         )

<强>解决方案:

$ refreshToken = $ accessToken [&#34; access_token&#34;] [&#34; refresh_token&#34;];

就在这一行之前:

    $client->fetchAccessTokenWithRefreshToken($refreshToken);

我可以在一小时后到期时根据需要刷新令牌。我认为本文的开发人员假设数组打印为:

<强>阵         (             [access_token] =&gt; XYZ123             [token_type] =&gt;来人             [expires_in] =&gt; 3600             [refresh_token] =&gt; xsss112222             [created] =&gt; 1511379484         )

所以他们认为你可以简单地做$ accessToken [&#34; refresh_token&#34;];这是不正确的。

现在我们有$ refreshToken的有效值,所以如果你这样做,错误就会消失。我还通过反馈链接更新了作者,让他们知道这个,以防其他php开发者遇到这个问题。希望这有助于某人。如果我把这篇文章格式化得不好,我很抱歉,我是S.E.的新手。我只是想分享,因为我终于让它工作了。

答案 5 :(得分:1)

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

全功能

#admin,#boundaries-claimed["@relations"=~'.*"claimed_by":[ ]*"IN".*'] {
  opacity: 0.5;
  line-join: round;
  line-color: #446;
  [maritime=1] {
    // downplay boundaries that are over water
    line-color: @water;
  }
  // Countries
  [admin_level=2],#boundaries-claimed {
    line-width: 0.8;
    line-cap: round;
    [zoom>=4] { line-width: 1.2; }
    [zoom>=6] { line-width: 2; }
    [zoom>=8] { line-width: 4; }
    [disputed=1] { line-dasharray: 4,4; line-opacity:0.5}
  }
  // States / Provices / Subregions
  [admin_level>=3] {
    line-width: 0.3;
    line-dasharray: 10,3,3,3;
    [zoom>=6] { line-width: 1; }
    [zoom>=8] { line-width: 1.5; }
    [zoom>=12] { line-width: 2; }
  }
}

#boundaries-disputed["disputed_by"=~'.*IN.*'] {
  opacity: 1;
  line-join: round;
  line-color: @land;
  [maritime=1] {
    // downplay boundaries that are over water
    line-color: @water;
  }
  // Countries
  [admin_level=2] {
    line-width: 1;
    line-cap: round;
    [zoom>=4] { line-width: 1.5; }
    [zoom>=6] { line-width: 3; }
    [zoom>=8] { line-width: 5; }
  }
}

答案 6 :(得分:0)

Google更新了他们的answered by another thread,并采用了改进的方法来解决这个问题:

以下代码段:

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

答案 7 :(得分:0)

就我而言,我忘了将访问类型设置为&#34;离线&#34;没有它,就没有生成刷新令牌。

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

答案 8 :(得分:0)

所以经过一段时间查看此代码:

// 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-&gt; getRefreshToken()返回null,如果你直接提供$ accessToken,它会正常工作并更新你的文件,希望它能解决某个问题。