我一直在玩Google AnalyticsAPI(V3)并遇到了som错误。首先,一切都设置正确,并与我的测试帐户一起使用。但是,当我想从其他个人资料ID(相同的Google Accont / GA帐户)中获取数据时,我收到403错误。奇怪的是,来自某些GA帐户的数据将返回数据,而其他帐户会产生此错误。
我已经撤销了令牌并再次进行了身份验证,现在看来我可以从我的所有帐户中获取数据。问题解决了?不。由于访问密钥将过期,我将再次遇到同样的问题。
如果我理解正确,可以使用resfreshToken获取新的authenticationTooken。
问题是,当我跑:
$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
这是一个错误,还是我完全误解了什么?
答案 0 :(得分:72)
所以我终于想出了如何做到这一点。基本的想法是,您有第一次请求身份验证时获得的令牌。该第一个令牌具有刷新令牌。第一个原始令牌在一小时后到期。一小时后,您必须使用第一个令牌中的刷新令牌来获取新的可用令牌。您使用$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);
答案 1 :(得分:39)
问题出在刷新令牌中:
[refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
如果带有'/'
的字符串获得json encoded
,则会使用'\'
进行转义,因此您需要将其删除。
您的案例中的刷新令牌应为:
1/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
我假设您已经完成的是您已经打印了谷歌发回的json字符串并将其复制并粘贴到您的代码中,因为如果您json_decode
它,那么它将正确删除{ {1}}对你而言!
答案 2 :(得分:17)
访问类型应设置为offline
。 state
是您为自己使用而设置的变量,而不是API的使用。
确保您拥有latest version of the client library并添加:
$client->setAccessType('offline');
有关参数的说明,请参阅Forming the URL。
答案 3 :(得分: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()
答案 4 :(得分:14)
@ uri-weg发布的答案对我有用,但由于我没有清楚地找到他的解释,让我重新说一下。
在第一个访问权限序列中,在回调中,当您到达收到身份验证代码的位置时,您必须保存访问令牌和刷新令牌。
原因是google api仅在提示访问权限时才向您发送带有刷新令牌的访问令牌。下一个访问令牌将在没有任何刷新令牌的情况下发送(除非您使用ddee(NR) | def(NR) | oqq | pqq
选项)。
您第一次收到的刷新令牌在用户撤消访问权限之前一直有效。
在简单的php中,回调序列的一个例子是:
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']);
答案 5 :(得分: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;
}
答案 6 :(得分:6)
有同样的问题;我的剧本昨天有效,因为一些奇怪的原因今天没有。没有变化。
显然这是因为我的系统时钟关闭了2.5(!!)秒,与NTP同步修复了它。
另请参阅:https://code.google.com/p/google-api-php-client/wiki/OAuth2#Solving_invalid_grant_errors
答案 7 :(得分:3)
仅供参考:3.0 Google Analytics(分析)API会在访问令牌过期时自动刷新访问令牌,因此您的脚本永远不需要refreshToken
。
(请参阅Sign
中的auth/apiOAuth2.php
功能)
答案 8 :(得分:3)
有时刷新令牌不是使用$client->setAccessType ("offline");
生成的。
试试这个:
$client->setAccessType ("offline");
$client->setApprovalPrompt ("force");
答案 9 :(得分:2)
我使用智能代码的示例和当前版本的Google API,但那个没有用。我认为他的API太过时了。
所以,我刚刚编写了自己的版本,基于其中一个API示例...它输出访问令牌,请求令牌,令牌类型,ID令牌,到期时间和创建时间作为字符串
如果您的客户端凭据和开发人员密钥正确无误,则此代码应该是开箱即用的。
<?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>
答案 10 :(得分:1)
我使用google-api-php-client v2.2.2,如果没有参数的函数调用会返回带有fetchAccessTokenWithRefreshToken();
的新令牌,它会返回更新的访问令牌并且刷新的令牌不会丢失。
if ($client->getAccessToken() && $client->isAccessTokenExpired()) {
$new_token=$client->fetchAccessTokenWithRefreshToken();
$token_data = $client->verifyIdToken();
}
答案 11 :(得分:1)
我对 google / google-api-php-client 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()));
}
答案 12 :(得分: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();
}
?>
答案 13 :(得分:0)
您需要在初始授权请求期间将访问令牌作为json字符串保存到文件或数据库,并将访问类型设置为离线$client->setAccessType("offline")
然后,在后续的api请求期间,从文件或数据库中获取访问令牌并将其传递给客户端:
$accessToken = json_decode($row['token'], true);
$client->setAccessToken($accessToken);
现在,您需要检查令牌是否已过期:
if ($client->isAccessTokenExpired()) {
// access token has expired, use the refresh token to obtain a new one
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
// save the new token to file or db
// ...json_encode($client->getAccessToken())
fetchAccessTokenWithRefreshToken()
函数将为您完成工作并提供新的访问令牌,然后将其保存回您的文件或数据库中。
答案 14 :(得分: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);
}
}
}
答案 15 :(得分:0)
根据Authentication on google: OAuth2 keeps returning 'invalid_grant'
“您应该重用第一次成功验证后获得的访问令牌。如果您之前的令牌尚未过期,您将收到invalid_grant错误。请将其缓存到某处以便重复使用。”
希望有所帮助
答案 16 :(得分:-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'];
?>