douzhu3654
2016-01-26 08:17
浏览 210
已采纳

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

I'm trying to connect my webapp to google drive. So I'm using PHP with official Github PHP client code [ https://github.com/google/google-api-php-client/tree/v1-master ].

I followed the quickstart [ https://developers.google.com/drive/v2/web/quickstart/php ] for v2, because PHP client is for v2 only.

Then I added a line to request offline access. [See https://developers.google.com/identity/protocols/OAuth2WebServer#offline]

My app code, developed using Yii 1, but it's not important, is:

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

This is the code for handling the OAuth callback. I simply set the access token received, then redirect to the page.

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

When user the first time access the page, my webapp sucessfully redirect to Google Login; user do login, and Google redirect user to my website at site/googleApiLoginCallback. I set the received code as accessToken and redirect user to the page of webapp he come from.

It works.

BUT: After a while, when user came back to the page, tyhe token is expired. When it's executed the $client->getRefreshToken(), it returns a null, so $client->refreshToken() throw the following error because of missing refresh token

Error refreshing the OAuth2 token, message: '{ "error" : "invalid_request", "error_description" : "Missing required parameter: refresh_token" }'

What am I missing or doing wrong?

For reference: this is my json access token. As you can see I've not a field named 'refreshToken' as I expect

{"access_token":"...hiddden...","token_type":"Bearer","expires_in":3600,"created":1453759023}
  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

2条回答 默认 最新

  • dongzhuo2371 2016-01-26 09:15
    已采纳

    From this StackOverflow question I see that statement

    in order to obtain a new refresh_token after already receiving one, you will need to send your user back through the prompt, which you can do by setting approval_prompt to force.

    It pointed to this old blog post by Google.

    So I added

    $client->setApprovalPrompt('force');
    

    after

    $client->setAccessType('offline');
    

    And now I've the resfresh token.

    点赞 打赏 评论
  • doubianyan9749 2016-01-26 08:52

    I am using a bit different logic, but it works... :-)
    Instead of:

    ...
    $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());
    }
    ...
    

    I do:

    ...
    $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 ...
    
    点赞 打赏 评论

相关推荐 更多相似问题