首页 文章

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

提问于
浏览
80

我一直在使用Google AnalyticsAPI(V3)并遇到了som错误 . 首先,一切都设置正确,并与我的测试帐户一起使用 . 但是,当我想从其他 Profiles 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

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

15 回答

  • 17

    所以我终于想出了如何做到这一点 . 基本的想法是,您有第一次请求身份验证时获得的令牌 . 该第一个令牌具有刷新令牌 . 第一个原始令牌在一小时后到期 . 一小时后,您必须使用第一个令牌中的刷新令牌来获取新的可用令牌 . 您使用 $client->refreshToken($refreshToken) 来检索新令牌 . 我将调用此"temp token."您还需要存储此临时令牌,因为一小时后它也会过期并注意它没有与之关联的刷新令牌 . 为了获得新的临时令牌,您需要使用之前使用的方法并使用第一个令牌的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);
    
  • 0

    问题出在刷新令牌中:

    [refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
    

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

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

    1/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
    

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

  • 3

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

    确保你有latest version of the client library并添加:

    $client->setAccessType('offline');
    

    有关参数的说明,请参见Forming the URL .

  • 7

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

    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()
    
  • 1

    @ uri-weg发布的答案对我有用,但由于我没有发现他的解释非常清楚,让我稍微改写一下 .

    在第一个访问权限序列期间,在回调中,当您到达收到身份验证代码的位置时,您也必须 save the access token and the refresh token .

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

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

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

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

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

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

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

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

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

  • 3

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

    (参见 auth/apiOAuth2.php 中的 Sign 函数)

  • 38

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

    试试这个:

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

    我使用智能代码的示例和当前版本的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 . '
    '; echo "Refresh Token = " . $token->refresh_token . '
    '; echo "Token type = " . $token->token_type . '
    '; echo "Expires in = " . $token->expires_in . '
    '; echo "ID Token = " . $token->id_token . '
    '; echo "Created = " . $token->created . '
    '; echo "<a class='logout' href='?logout'>Logout</a>"; } else { $authUrl = $client->createAuthUrl(); print "<a class='login' href='$authUrl'>Connect Me!</a>"; } ?> </body> </html>
  • 0

    我有一个相同的问题与谷歌/谷歌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()));
        }
    
  • 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();
    }
    ?>
    
  • 72

    根据Authentication on google: OAuth2 keeps returning 'invalid_grant'

    “您应该重用第一次成功验证后获得的访问令牌 . 如果您之前的令牌尚未过期,您将收到invalid_grant错误 . 请将其缓存到某处,以便您可以重复使用它 . ”

    希望能帮助到你

  • 6

    自此问题最初发布以来,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);
            }
        }
    
    
    }
    
  • -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'];
        ?>
    

相关问题