Google API:缺少refresh_token(访问类型 = 离线)


Google API: refresh_token missing (access type = offline)

我正在尝试将我的网络应用程序连接到谷歌云端硬盘。所以我使用PHP和官方的Github PHP客户端代码[https://github.com/google/google-api-php-client/tree/v1-master]。

我遵循了 v2 的快速入门 [ https://developers.google.com/drive/v2/web/quickstart/php ],因为 PHP 客户端仅适用于 v2。

然后我添加了一行来请求离线访问。[见 https://developers.google.com/identity/protocols/OAuth2WebServer#offline]

我的应用程序代码,使用 Yii 1 开发,但它并不重要,是:

    $client = new Google_Client();
    $client->setApplicationName("Google Drive Client");
    $client->addScope(Google_Service_Drive::DRIVE_METADATA_READONLY);
    $client->setRedirectUri( Yii::app()->createAbsoluteUrl("site/googleApiLoginCallback") );    
    $client->setAuthConfigFile(CLIENT_SECRET_PATH);
    $client->setAccessType('offline');
    if (file_exists(CREDENTIALS_PATH)) {
        $accessToken = file_get_contents(CREDENTIALS_PATH);
    } else {
        // Request authorization from the user.
        $auth_url = $client->createAuthUrl();
        header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
        Yii::app()->end();
    }   
    $client->setAccessToken($accessToken);
    // Refresh the token if it's expired.
    if ($client->isAccessTokenExpired()) {
        $refresh_token = $client->getRefreshToken();
        // CVarDumper::dump($refresh_token,2,true);
        $client->refreshToken($refresh_token);
        file_put_contents(CREDENTIALS_PATH, $client->getAccessToken());
    }
    return $client;

这是用于处理 OAuth 回调的代码。我只需设置收到的访问令牌,然后重定向到该页面。

public function actionGoogleApiLoginCallback($code)
{
    $client = new Google_Client();
    $client->setApplicationName("Google Drive Client");
    $client->addScope(Google_Service_Drive::DRIVE_METADATA_READONLY);
    $client->setRedirectUri( Yii::app()->createAbsoluteUrl("site/googleApiLoginCallback") );    
    $client->setAuthConfigFile(CLIENT_SECRET_PATH);
    $client->setAccessType('offline');
    $accessToken = $client->authenticate($code);
    if(!file_exists(dirname(CREDENTIALS_PATH))) {
      mkdir(dirname(CREDENTIALS_PATH), 0700, true);
    }
    file_put_contents(CREDENTIALS_PATH, $accessToken);
    $preGoogleApiLoginRoute = Yii::app()->user->getState("preGoogleApiLoginRoute", null);
    if ($preGoogleApiLoginRoute) 
    {
        $this->redirect(array( $preGoogleApiLoginRoute ));
    } else  {
        $this->redirect(array("site/index"));
    }
}

当用户第一次访问该页面时,我的网络应用程序成功重定向到Google Login;用户登录,Google将用户重定向到我的网站site/googleApiLoginCallback。我将收到的代码设置为访问令牌,并将用户重定向到他来自的网络应用程序页面。

它有效。

但是:一段时间后,当用户返回到页面时,令牌已过期。当它执行$client->getRefreshToken()时,它返回一个null,所以$client->refreshToken()因为缺少刷新令牌而抛出以下错误

刷新 OAuth2 令牌时出错,消息:"{ "错误" : "invalid_request", "error_description" : "缺少必需参数: refresh_token" }'

我错过了什么或做错了什么?

供参考:这是我的 json 访问令牌。如您所见,我没有像我期望的那样名为"刷新令牌"的字段

{"access_token":"...hiddden...","token_type":"Bearer","expires_in":3600,"created":1453759023}

从这个 StackOverflow 问题中,我看到了该语句

为了在收到新refresh_token后获得新,您需要通过提示将用户发送回去,您可以通过将approval_prompt设置为 force 来完成。

它指向了谷歌的这篇旧博客文章。

所以我补充说

$client->setApprovalPrompt('force');

$client->setAccessType('offline');

现在我有了新鲜的令牌。

我使用的逻辑有点不同,但它有效... :-)
而不是:

...
$accessToken = file_get_contents(CREDENTIALS_PATH);
...
$client->setAccessToken($accessToken);
if ($client->isAccessTokenExpired()) {
    $refresh_token = $client->getRefreshToken();
    $client->refreshToken($refresh_token);
    file_put_contents(CREDENTIALS_PATH, $client->getAccessToken());
}
...

我愿意:

...
$accessToken = file_get_contents(CREDENTIALS_PATH);
...
$client->setAccessToken($accessToken);
if (!$client->getAccessToken()) {
    die('invalid access token in ' . CREDENTIALS_PATH);
}
if ($client->isAccessTokenExpired()) {
    $refresh_token = json_decode($accessToken)->refresh_token;
    $client->refreshToken($refresh_token);
}
... now we are authenticated ...