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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9241213/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 06:25:13  来源:igfitidea点击:

How to refresh token with Google API client?

phpgoogle-apioauth-2.0access-tokengoogle-analytics-api

提问by seorch.me

I've been playing around with the Google Analytics API (V3) and have run into som errors. Firstly, everything is set up correct and worked with my testing account. But when I want to grab data from another profile ID (Same Google Accont/GA Account) I get an 403 Error. The strange thing is that data from some GA accounts will return data whilst other generate this error.

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

I've revoked the token and authenticated one more time, and now it seems like I can grab data from all of my accounts. Problem solved? Not. As the access key will expire, I will run into the same issue again.

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

If I have understood things right, one could use the resfreshToken to get a new authenticationTooken.

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

The problem is, when I run:

问题是,当我运行时:

$client->refreshToken(refresh_token_key) 

the following error is returned:

返回以下错误:

Error refreshing the OAuth2 token, message: '{ "error" : "invalid_grant" }'

I've checked the code behind the refreshToken method and tracked the request back to the “apiOAuth2.php” file. All parameters are sent correctly. The grant_type is hard coded to ‘refresh_token' within the method, so it's hard for me to understand what's wrong. The parameter array looks like this:

我检查了 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 )

The procedure is as follows.

程序如下。

$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

Is this a bug, or have I completely misunderstood something?

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

回答by Uri Weg

So i finally figured out how to do this. The basic idea is that you have the token you get the first time you ask for authentication. This first token has a refresh token. The first original token expires after an hour. After an hour you have to use the refresh token from the first token to get a new usable token. You use $client->refreshToken($refreshToken)to retrieve a new token. I will call this "temp token." You need to store this temp token as well because after an hour it expires as well and note it does not have a refresh token associated with it. In order to get a new temp token you need to use the method you used before and use the first token's refreshtoken. I have attached code below, which is ugly, but im new at this...

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

回答by Asim

The problem is in the refresh token:

问题出在刷新令牌中:

[refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY

When a string with a '/'gets json encoded, It is escaped with a '\', hence you need to remove it.

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

The refresh token in your case should be:

您的情况下的刷新令牌应该是:

1/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY

What i'm assuming you've done is that you've printed the json string which google sent back and copied and pasted the token into your code because if you json_decodeit then it will correctly remove the '\'for you!

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

回答by Faishal

here is the snippet to set token, before that make sure the access type should be set to offline

这是设置令牌的片段,在此之前确保访问类型应设置为离线

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

To refresh token

刷新令牌

$google_token= json_decode($_SESSION['access_token']);
$client->refreshToken($google_token->refresh_token);

this will refresh your token, you have to update it in session for that you can do

这将刷新您的令牌,您必须在会话中更新它才能做到

 $_SESSION['access_token']= $client->getAccessToken()

回答by jk.

The access type should be set to offline. stateis a variable you set for your own use, not the API's use.

访问类型应设置为offlinestate是您为自己使用而设置的变量,而不是 API 的使用。

Make sure you have the latest version of the client libraryand add:

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

$client->setAccessType('offline');

See Forming the URLfor an explanation of the parameters.

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

回答by Daishi

The answer posted by @uri-weg worked for me but as I did not find his explanations very clear, let me reword it a little.

@uri-weg 发布的答案对我有用,但由于我觉得他的解释不是很清楚,让我稍微改写一下。

During the first access permission sequence, in the callback, when you get to the point where you receive an authentication code, you must save the access token and the refresh tokenas well.

在第一个访问权限序列中,在回调中,当您到达接收身份验证代码的位置时,您还必须保存访问令牌和刷新令牌

The reason is google api sends you an access token with a refresh token only when prompting for access permission. The next access tokens will be sent without any refresh token (unless you use the approval_prompt=forceoption).

原因是 google api 仅在提示访问权限时才会向您发送带有刷新令牌的访问令牌。下一个访问令牌将在没有任何刷新令牌的情况下发送(除非您使用该approval_prompt=force选项)。

The refresh token you received the first time stays valid until the user revokes access permission.

您第一次收到的刷新令牌一直有效,直到用户撤销访问权限。

In simplistic php, an example of the callback sequence would be:

在简单的 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']);

And later on, in simplistic php, the connection sequence would be:

后来,在简单的 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());
}

回答by Mr_Green

Here is the code which I am using in my project and it is working fine:

这是我在我的项目中使用的代码,它工作正常:

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

回答by strikernl

Had the same issue; my script that worked yesterday, for some odd reason did not today. No changes.

有同样的问题;我昨天工作的脚本,由于某种奇怪的原因今天没有。没有变化。

Apparently this was because my system clock was off by 2.5 (!!) seconds, syncing with NTP fixed it.

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

See also: https://code.google.com/p/google-api-php-client/wiki/OAuth2#Solving_invalid_grant_errors

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

回答by Meenu Sharma

Sometimes Refresh Token i not generated by using $client->setAccessType ("offline");.

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

Try this:

尝试这个:

$client->setAccessType ("offline");
$client->setApprovalPrompt ("force"); 

回答by Mark Smith

FYI: The 3.0 Google Analytics API will automatically refresh the access token if you have a refresh token when it expires so your script never needs refreshToken.

仅供参考:如果您在访问令牌过期时有刷新令牌,3.0 Google Analytics API 将自动刷新访问令牌,因此您的脚本永远不需要refreshToken.

(See the Signfunction in auth/apiOAuth2.php)

(见 中的Sign功能auth/apiOAuth2.php

回答by John Slegers

I used the example by smartcodes with the current version of the Google API, but that one didn't work. I think his API is too outdated.

我在当前版本的 Google API 中使用了 smartcodes 的示例,但那个没有用。我认为他的 API 太过时了。

So, I just wrote my own version, based on one of the API examples... It outputs access token, request token, token type, ID token, expiration time and creation time as strings

所以,我只是根据 API 示例之一编写了自己的版本......它以字符串形式输出访问令牌、请求令牌、令牌类型、ID 令牌、到期时间和创建时间

If your client credentials and developer key are correct, this code should work out of the box.

如果您的客户端凭据和开发人员密钥正确,则此代码应该可以立即使用。

<?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>