如何使用Google API客户端刷新令牌?

seo*_*.me 86 php google-api google-analytics-api access-token oauth-2.0

我一直在使用Google AnalyticsAPI(V3)并遇到了som错误.首先,一切都设置正确,并与我的测试帐户一起使用.但是,当我想从其他个人资料ID(相同的Google Accont/GA帐户)中获取数据时,我收到403错误.奇怪的是,来自某些GA帐户的数据将返回数据,而其他帐户会生成此错误.

我已经撤销了令牌并再次进行了身份验证,现在看来我可以从我的所有帐户中获取数据.问题解决了?不.由于访问密钥将过期,我将再次遇到同样的问题.

如果我理解正确,可以使用resfreshToken来获取新的authenticationTooken.

问题是,当我跑:

$client->refreshToken(refresh_token_key) 
Run Code Online (Sandbox Code Playgroud)

返回以下错误:

Error refreshing the OAuth2 token, message: '{ "error" : "invalid_grant" }'
Run Code Online (Sandbox Code Playgroud)

我检查了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 )
Run Code Online (Sandbox Code Playgroud)

程序如下.

$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
Run Code Online (Sandbox Code Playgroud)

这是一个错误,还是我完全误解了什么?

小智 74

所以我终于想出了如何做到这一点.基本的想法是,您有第一次请求身份验证时获得的令牌.该第一个令牌具有刷新令牌.第一个原始令牌在一小时后到期.一小时后,您必须使用第一个令牌中的刷新令牌来获取新的可用令牌.您$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);
Run Code Online (Sandbox Code Playgroud)

  • 而不是检查3600秒,你应该使用$ client-> isAccessTokenExpired() (50认同)
  • 小更新。在最新版本中,当您请求刷新令牌时,返回的新访问令牌现在带有新的刷新令牌。所以本质上,您可以使用更新后的 json 令牌来替换之前的 json 令牌,而不再需要保留初始访问令牌。. (3认同)
  • 非常感谢你... :) (2认同)

小智 41

问题出在刷新令牌中:

[refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
Run Code Online (Sandbox Code Playgroud)

当带有a的字符串'/'获取时json encoded,它会被a转义'\',因此您需要将其删除.

您的案例中的刷新令牌应为:

1/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
Run Code Online (Sandbox Code Playgroud)

我假设你已经完成的是你已经打印了谷歌发回的json字符串并将其复制并粘贴到你的代码中,因为如果你json_decode这样,那么它将正确地'\'为你删除!


Str*_*k3r 18

这是设置令牌的代码段,在此之前确保应将访问类型设置为脱机

if (isset($_GET['code'])) {
  $client->authenticate();
  $_SESSION['access_token'] = $client->getAccessToken();
}
Run Code Online (Sandbox Code Playgroud)

刷新令牌

$google_token= json_decode($_SESSION['access_token']);
$client->refreshToken($google_token->refresh_token);
Run Code Online (Sandbox Code Playgroud)

这将刷新你的令牌,你必须在会话中更新它,你可以做

 $_SESSION['access_token']= $client->getAccessToken()
Run Code Online (Sandbox Code Playgroud)


jk.*_*jk. 17

访问类型应设置为offline.state是您为自己使用而设置的变量,而不是API的用途.

确保您拥有最新版本的客户端库并添加:

$client->setAccessType('offline');
Run Code Online (Sandbox Code Playgroud)

有关参数的说明,请参阅形成URL.


Dai*_*shi 14

@ uri-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']);
Run Code Online (Sandbox Code Playgroud)

稍后,在简单的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());
}
Run Code Online (Sandbox Code Playgroud)


Mr_*_*een 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;
}
Run Code Online (Sandbox Code Playgroud)


str*_*rnl 6

有同样的问题; 我的剧本昨天有效,因为一些奇怪的原因今天没有.没有变化.

显然这是因为我的系统时钟关闭了2.5(!!)秒,与NTP同步修复了它.

另请参阅:https://code.google.com/p/google-api-php-client/wiki/OAuth2#Solving_invalid_grant_errors


小智 5

有时刷新令牌我不是通过使用生成的$client->setAccessType ("offline");

尝试这个:

$client->setAccessType ("offline");
$client->setApprovalPrompt ("force"); 
Run Code Online (Sandbox Code Playgroud)