我目前面临一个非常奇怪的问题,实际上我一直在从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);
}
因此,为了检查我是否错了,或者谷歌是否,我做了一个实验:昨天晚上我从ssh启动快速启动脚本来检查它是否正常工作,确实是这样,所以我决定今天早上查看它是否仍在工作就像我睡觉前一样,事实并非如此,我认为谷歌的quickstart.php
有问题。
我希望有人可以帮助我,我已经检查过有关该主题的所有其他帖子,但它们都已过时了。
我最近遇到了同样的问题,我解决了这个问题。
<?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;
}
我在新的谷歌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));
}
我的建议是在获取访问令牌后立即将刷新令牌保存到.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;
}
对于任何遇到此消息问题的人来说只是一些更新,主要是因为只有第一个命令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));
}
现在每次更新访问令牌时,您的刷新令牌也会被传递下来。
我有同样的问题,最后去工作:
背景故事:
我收到了同样的错误。这是我发现的:
这个错误:
PHP致命错误:未捕获LogicException:必须在/Library/WebServer/Documents/Sites/test/scripts/vendor/google/apiclient/src/Google/Client.php:267中传递或设置刷新令牌作为setAccessToken的一部分
引用更新访问令牌(又称Refresh)方法:
$client->fetchAccessTokenWithRefreshToken($refreshTokenSaved);
为什么失败了?长话短说,我意识到当我打印出$ accessToken数组时,它来自解码这个json文件(根据你发布的快速启动代码/来自google)
凭据/日历-PHP-quickstart.json
我发现错误是由于在print_r时accessToken数组的打印方式:
数组([access_token] =>数组([access_token] => xyz123 [token_type] =>承载[expires_in] => 3600 [refresh_token] => xsss112222 [created] => 1511379484)
)
解:
$ refreshToken = $ accessToken [“access_token”] [“refresh_token”];
就在这一行之前:
$client->fetchAccessTokenWithRefreshToken($refreshToken);
我可以在一小时内到期时根据需要刷新令牌。我认为这篇文章的开发人员假设数组打印为:
数组([access_token] => xyz123 [token_type] =>承载[expires_in] => 3600 [refresh_token] => xsss112222 [created] => 1511379484)
所以他们认为你可以简单地做$ accessToken [“refresh_token”];这是不正确的。
现在我们有一个$ refreshToken的有效值,所以如果你这样做,错误就会消失。我还通过反馈链接更新了作者,让他们知道这个,以防其他php开发者遇到这个问题。希望这有助于某人。如果我把这篇文章格式化得不好,我很抱歉,我是S.E.的新手。我只是想分享,因为我终于让它工作了。
当你把它写到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;
}
谷歌已经更新了他们的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()));
}
在我的情况下,我忘记将访问类型设置为“脱机”,否则不会生成刷新令牌。
$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()));
}
所以经过一段时间查看此代码:
// 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,它会正常工作并更新你的文件,希望它能解决某个问题。