Google drive api refresh token issue [duplicate] - php

This question already has answers here:
How to refresh token with Google API client?
(18 answers)
Closed 4 years ago.
I am working on google drive API to create spreadsheet. First time when API authenticate with my gmail account it return me following information:
{
"access_token":"XXXXXXXXXs7ZqxIslxBnkLXXXXXJOvMjULXXXXXXXXXXX",
"expires_in":3600,
"scope":"https:\/\/www.googleapis.com\/auth\/spreadsheets",
"token_type":"Bearer",
"created":1536680902
}
Basically, it allow me to use access token only for 1 hour, after expiration of access token when I try to refresh token it's giving me below error:
Fatal error: Uncaught exception 'LogicException' with message 'refresh token must be passed in or set as part of setAccessToken' in C:\xampp\htdocs\drivegoogle\src\Google\Client.php:267
Below are my code that I am running to get access and refresh token:
function getClient() {
$client = new Google_Client();
$client->setApplicationName('Google Sheets API PHP Quickstart');
$client->setScopes(Google_Service_Sheets::SPREADSHEETS);
// $client->setScopes(Google_Service_Sheets::SPREADSHEETS_READONLY);
$client->setAuthConfig('credentials.json');
$client->setRedirectUri("http://localhost/drivegoogle/index.php");
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
// Load previously authorized credentials from a file.
if (file_exists('1536680902.json')) {
$accessToken = json_decode(file_get_contents('1536680902.json'),
true);
} else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
header('Location: ' . filter_var($authUrl, FILTER_SANITIZE_URL));
if (isset($_GET['code'])) {
$authCode = $_GET['code'];
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
header('Location: ' . filter_var($this->redirectUri,
FILTER_SANITIZE_URL));
if(!file_exists(dirname('1536680902.json'))) {
mkdir(dirname('1536680902.json'), 0700, true);
}
file_put_contents($this->tokenFile, json_encode($accessToken));
}else{
exit('No code found');
}
}
$client->setAccessToken($accessToken);
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
// save refresh token to some variable
$refreshTokenSaved = $client->getRefreshToken();
// update access token
$client->fetchAccessTokenWithRefreshToken($refreshTokenSaved);
// pass access token to some variable
$accessTokenUpdated = $client->getAccessToken();
// append refresh token
$accessTokenUpdated['refresh_token'] = $refreshTokenSaved;
//Set the new acces token
$accessToken = $refreshTokenSaved;
$client->setAccessToken($accessToken);
// save to file
file_put_contents($this->tokenFile,
json_encode($accessTokenUpdated));
}
}
Any body please help me to sort out problem. Thanks

Try with Below Code,
function getClient() {
$client = new Google_Client();
$client->setApplicationName('Google Sheets API PHP Quickstart');
$client->setScopes(Google_Service_Sheets::SPREADSHEETS);
// $client->setScopes(Google_Service_Sheets::SPREADSHEETS_READONLY);
$client->setAuthConfig('credentials.json');
$client->setRedirectUri("http://localhost/drivegoogle/index.php");
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
// Load previously authorized credentials from a file.
if (file_exists('1536680902.json')) {
$accessToken = json_decode(file_get_contents('1536680902.json'),
true);
} else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
header('Location: ' . filter_var($authUrl, FILTER_SANITIZE_URL));
if (isset($_GET['code'])) {
$authCode = $_GET['code'];
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
header('Location: ' . filter_var($this->redirectUri,
FILTER_SANITIZE_URL));
if(!file_exists(dirname('1536680902.json'))) {
mkdir(dirname('1536680902.json'), 0700, true);
}
file_put_contents($this->tokenFile, json_encode($accessToken));
}else{
exit('No code found');
}
}
$accessToken = $client->setAccessToken($accessToken);
$decoded_accessToken= json_decode($accessToken);
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
// save refresh token to some variable
$refreshTokenSaved = $client->getRefreshToken($decoded_accessToken->refresh_token);
// update access token
$client->fetchAccessTokenWithRefreshToken($refreshTokenSaved);
// pass access token to some variable
$accessTokenUpdated = $client->getAccessToken();
// append refresh token
$accessTokenUpdated['refresh_token'] = $refreshTokenSaved;
//Set the new acces token
$accessToken = $refreshTokenSaved;
$client->setAccessToken($accessToken);
// save to file
file_put_contents($this->tokenFile,
json_encode($accessTokenUpdated));
}
}

Related

Google Calendar API Token not refreshing

For some reason when my token expires I need to delete the file and reconnect again otherwise nothing is working. Is it because the new token is not stored or?
I have the following code:
if(!$_GET['id']){
echo print_r('BUSINESS NOT FOUND');
exit();
}else {
$client = new Google_Client();
$client->setApplicationName('Google Calendar API PHP Quickstart');
$client->setScopes(Google_Service_Calendar::CALENDAR);
$client->setAuthConfig(__DIR__ . '/client.json');
$client->setAccessType('offline');
$client->setPrompt('select_account consent');
// Load previously authorized token from a file, if it exists.
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
$tokenPath = '../Remindly/accounts/' . $_GET['id'] . '.json';
if (file_exists($tokenPath)) {
$accessToken = json_decode(file_get_contents($tokenPath), true);
$client->setAccessToken($accessToken);
return $client;
}
// If there is no previous token or it's expired.
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
}
// Save the token to a file.
file_put_contents($tokenPath, json_encode($client->getAccessToken()));
}
}
It seems that you are not returning client in case the token expired
Modify your code as shown in the quickstart for PHP to return the client at the end of the function - both if the access token expired and if it did not:
...
if(...){
...
}else{
$client = new Google_Client();
$client->setApplicationName('Google Calendar API PHP Quickstart');
$client->setScopes(Google_Service_Calendar::CALENDAR_READONLY);
$client->setAuthConfig('credentials.json');
$client->setAccessType('offline');
$client->setPrompt('select_account consent');
// Load previously authorized token from a file, if it exists.
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
$tokenPath = 'token.json';
if (file_exists($tokenPath)) {
$accessToken = json_decode(file_get_contents($tokenPath), true);
$client->setAccessToken($accessToken);
}
// If there is no previous token or it's expired.
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
} else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
print 'Enter verification code: ';
$authCode = trim(fgets(STDIN));
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Check to see if there was an error.
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
}
}
// Save the token to a file.
if (!file_exists(dirname($tokenPath))) {
mkdir(dirname($tokenPath), 0700, true);
}
file_put_contents($tokenPath, json_encode($client->getAccessToken()));
}
return $client;
}
...

Permanent authorization token Google API Sheets for php

I'm working on a project where you need to use Google Api for Google Sheets service to make changes by means of php, but my access token obtained through clicking on the authorization link , lives only 2 hours. The problem is to extend the validity of the token for more than 2 hours, please help with this task.The following is an example of my function to get a Google client:
$client = new Google_Client();
$client->setApplicationName('My Project 45639');
//$client->setScopes(Google_Service_Sheets::SPREADSHEETS);
$client->setAuthConfig('credentials.json');
$client->setAccessType('offline');
$client->setPrompt('select_account consent');
$client->setScopes('https://www.googleapis.com/auth/spreadsheets');
// Load previously authorized token from a file, if it exists.
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
$tokenPath = 'token.json';
if (file_exists($tokenPath)) {
$accessToken = json_decode(file_get_contents($tokenPath), true);
$client->setAccessToken($accessToken);
}
// If there is no previous token or it's expired.
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
} else {
// Request authorization from the user.
//$authUrl = $client->createAuthUrl();
//printf("%s", $authUrl);
$authCode = trim('4/aQHXC2H_MmBA3ifDlXmJiDHyEk2m2QCoKrP1YDdAbBZSZBPUMmbINxg');
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Check to see if there was an error.
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
}
}
// Save the token to a file.
if (!file_exists(dirname($tokenPath))) {
mkdir(dirname($tokenPath), 0700, true);
}
file_put_contents($tokenPath, json_encode($client->getAccessToken()));
}
return $client;

Google OAuth2 refresh token using PHP

I am trying to create and save an access token that I can reference when other users in my organization (who do not have API access) log in. For instance I need to call Google Admin API to grab their orgUnit. When I get my access_token I can use this with other users but it expires after an hour and I have to re-log in with my account to get a new token.
In Short, I am not sure where or how to create the refresh token so I can save it in my datbase.
//Create Client Request to access Google API
$client = new Google_Client();
$client->setApplicationName(APPLICATION_NAME);
$client->setClientId(CLIENT_ID);
$client->setClientSecret(CLIENT_SECRET);
$client->setRedirectUri(REDIRECT_URL);
$client->setDeveloperKey(API_KEY);
$client->setAccessType('offline');
$client->addScope("https://www.googleapis.com/auth/userinfo.email");
$client->addScope("https://www.googleapis.com/auth/admin.directory.user.readonly");
//Send Client Request
if($client->isAccessTokenExpired()){
//not doing anything here at the moment
}
$objOAuthService = new Google_Service_Oauth2($client);
//Logout
if (isset($_REQUEST['logout'])) {
unset($_SESSION['access_token']);
unset($_SESSION['code']);
$client->revokeToken();
header('Location: ' . filter_var(REDIRECT_URL, FILTER_SANITIZE_URL)); //redirect user back to page
}else if (isset($_GET['code'])) {
//Authenticate code from Google OAuth Flow
//Add Access Token to Session
$client->authenticate($_GET['code']);
$_SESSION['code'] = $_GET['code'];
$_SESSION['access_token'] = $client->getAccessToken();
header('Location: ' . filter_var(REDIRECT_URL, FILTER_SANITIZE_URL));
}
//Set Access Token to make Request
if (isset($_SESSION['access_token'])) {
$client->setAccessToken($_SESSION['access_token']);
}

Google API Client "refresh token must be passed in or set as part of setAccessToken"

I am currently facing a very strange problem, indeed I've been following this very same guide (https://developers.google.com/google-apps/calendar/quickstart/php) from Google API documentation. I tried it twice, at the first time it work like a charm but after the access token had expire the script provided straight by Google API Doc was unable to refresh it.
TL;DR
Here is the error message:
sam#ssh:~$ php www/path/to/app/public/quickstart.php
Fatal error: Uncaught exception 'LogicException' with message 'refresh token must be passed in or set as part of setAccessToken' in /home/pueblo/www/path/to/app/vendor/google/apiclient/src/Google/Client.php:258
Stack trace:
#0 /home/pueblo/www/path/to/app/public/quickstart.php(55): Google_Client->fetchAccessTokenWithRefreshToken(NULL)
#1 /home/pueblo/www/path/to/app/public/quickstart.php(76): getClient()
#2 {main}
thrown in /home/pueblo/www/path/to/app/vendor/google/apiclient/src/Google/Client.php on line 258
Here is the part of the php script from google I've modified:
require_once __DIR__ . '/../vendor/autoload.php';
// I don't want the creds to be in my home folder, I prefer them in the app's root
define('APPLICATION_NAME', 'LRS API Calendar');
define('CREDENTIALS_PATH', __DIR__ . '/../.credentials/calendar-php-quickstart.json');
define('CLIENT_SECRET_PATH', __DIR__ . '/../client_secret.json');
I also modified the expandHomeDirectory so I could "disable" it without modifying too much code:
function expandHomeDirectory($path) {
$homeDirectory = getenv('HOME');
if (empty($homeDirectory)) {
$homeDirectory = getenv('HOMEDRIVE') . getenv('HOMEPATH');
}
return $path;
// return str_replace('~', realpath($homeDirectory), $path);
}
So to check if I was wrong or if Google was, I did an experiment: yesterday night I launch the quickstart script from ssh to check if it was working, and indeed it was, so I decided to check this morning if it still working just as it was before I slept and it wasn't so I think there's something wrong with Google's quickstart.php.
I hope someone could help me, I already checked all the other posts about the subject but they are all outdated.
I got the same problem recently and i solved it with this.
<?php
$client->setRedirectUri($this->_redirectURI);
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
I explain .....
Refresh token is not returned because we didnt force the approvalPrompt. The offline mode is not enought. We must force the approvalPrompt. Also the redirectURI must be set before these two options. It worked for me.
This is my full function
<?php
private function getClient()
{
$client = new Google_Client();
$client->setApplicationName($this->projectName);
$client->setScopes(SCOPES);
$client->setAuthConfig($this->jsonKeyFilePath);
$client->setRedirectUri($this->redirectUri);
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
// Load previously authorized credentials from a file.
if (file_exists($this->tokenFile)) {
$accessToken = json_decode(file_get_contents($this->tokenFile),
true);
} else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
header('Location: ' . filter_var($authUrl, FILTER_SANITIZE_URL));
if (isset($_GET['code'])) {
$authCode = $_GET['code'];
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
header('Location: ' . filter_var($this->redirectUri,
FILTER_SANITIZE_URL));
if(!file_exists(dirname($this->tokenFile))) {
mkdir(dirname($this->tokenFile), 0700, true);
}
file_put_contents($this->tokenFile, json_encode($accessToken));
}else{
exit('No code found');
}
}
$client->setAccessToken($accessToken);
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
// save refresh token to some variable
$refreshTokenSaved = $client->getRefreshToken();
// update access token
$client->fetchAccessTokenWithRefreshToken($refreshTokenSaved);
// pass access token to some variable
$accessTokenUpdated = $client->getAccessToken();
// append refresh token
$accessTokenUpdated['refresh_token'] = $refreshTokenSaved;
//Set the new acces token
$accessToken = $refreshTokenSaved;
$client->setAccessToken($accessToken);
// save to file
file_put_contents($this->tokenFile,
json_encode($accessTokenUpdated));
}
return $client;
}
My advice is save refresh token to .json immediately after get access token and if access token expired use refresh token.
In my projects work this way:
public static function getClient()
{
$client = new Google_Client();
$client->setApplicationName('JhvInformationTable');
$client->setScopes(Google_Service_Calendar::CALENDAR_READONLY);
$client->setAuthConfig('credentials.json');
$client->setAccessType('offline');
// Load previously authorized credentials from a file.
$credentialsPath = 'token.json';
$credentialsPath2 = 'refreshToken.json';
if (file_exists($credentialsPath)) {
$accessToken = json_decode(file_get_contents($credentialsPath), true);
} else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
//printf("Open the following link in your browser:\n%s\n", $authUrl);
//print 'Enter verification code: ';
$authCode = trim(fgets(STDIN));
//echo "<script> location.href='".$authUrl."'; </script>";
//exit;
$authCode ='********To get code, please uncomment the code above********';
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$refreshToken = $client->getRefreshToken();
// Check to see if there was an error.
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
}
// Store the credentials to disk.
if (!file_exists(dirname($credentialsPath))) {
mkdir(dirname($credentialsPath), 0700, true);
}
file_put_contents($credentialsPath, json_encode($accessToken));
file_put_contents($credentialsPath2, json_encode($refreshToken));
printf("Credentials saved to %s\n", $credentialsPath);
}
$client->setAccessToken($accessToken);
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$refreshToken = json_decode(file_get_contents($credentialsPath2), true);
$client->fetchAccessTokenWithRefreshToken($refreshToken);
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
return $client;
}
I have faced the same issue with new google api library. Search for solution brought the following link:
RefreshToken Not getting send back after I get new token google sheets API
Based on that information, I have modified the quickstart code part to fit my needs. After first authorization with Google I have obtained drive-php-quickstart.json which contains refresh_token that expires in 3600 seconds or one hour. The refresh token is issued only once, so if it is lost, then re-authorization is required.
So, that to have it always in drive-php-quickstart.json I have done following:
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
// save refresh token to some variable
$refreshTokenSaved = $client->getRefreshToken();
// update access token
$client->fetchAccessTokenWithRefreshToken($refreshTokenSaved);
// pass access token to some variable
$accessTokenUpdated = $client->getAccessToken();
// append refresh token
$accessTokenUpdated['refresh_token'] = $refreshTokenSaved;
// save to file
file_put_contents($credentialsPath, json_encode($accessTokenUpdated));
}
just some update for anyone having trouble with this message, mostly it's because only first command fetchAccessTokenWithAuthCode() generates credencials that contains the refresh token (technically forever valid - no 2 hours validity if you dont revoke it). When you get the new one it replaces the original one yet it does not contains the refresh token that it needs so next time you need to update the token, it will crash. This can be easily fixed by replacing the refresh function to for example this:
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$oldAccessToken=$client->getAccessToken();
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
$accessToken=$client->getAccessToken();
$accessToken['refresh_token']=$oldAccessToken['refresh_token'];
file_put_contents($credentialsPath, json_encode($accessToken));
}
Now everytime you update the access token your refresh token is passed down too.
I had the same issues and finally go this to work:
Backstory:
I received the same error. Here is what I found:
This error:
PHP Fatal error: Uncaught LogicException: refresh token must be passed in or set as part of setAccessToken in /Library/WebServer/Documents/Sites/test/scripts/vendor/google/apiclient/src/Google/Client.php:267
Is referencing the update access token (aka Refresh) method:
$client->fetchAccessTokenWithRefreshToken($refreshTokenSaved);
Why was it failing? Long story short, I realized when I printed out the $accessToken array, which comes from decoding this json file (per the quickstart code you posted/that comes from google)
credentials/calendar-php-quickstart.json
I found the error due to how the accessToken array prints out when I print_r:
Array
(
[access_token] => Array
(
[access_token] => xyz123
[token_type] => Bearer
[expires_in] => 3600
[refresh_token] => xsss112222
[created] => 1511379484
)
)
Solution:
$refreshToken = $accessToken["access_token"]["refresh_token"];
right before this line:
$client->fetchAccessTokenWithRefreshToken($refreshToken);
I can finally refresh the token as needed when it expires in an hour. I think the devs of this article assumed the array prints as:
Array
(
[access_token] => xyz123
[token_type] => Bearer
[expires_in] => 3600
[refresh_token] => xsss112222
[created] => 1511379484
)
so they thought you could simply do $accessToken["refresh_token"]; That is not correct.
Now we have a valid value for $refreshToken, so the error should go away if you do this. I also updated the author via the feedback link to let them know about this, in case other php devs encounter this issue. Hopefully this helps somebody. My apologies if I formatted this post poorly I am new to S.E. I just wanted to share as I finally got this to work.
You need to serialize the accestoken when you write it to the credentialsPath.
// Exchange authorization code for an access token.
$accessToken = $client->authenticate($authCode);
// Store the credentials to disk.
if(!file_exists(dirname($credentialsPath))) {
mkdir(dirname($credentialsPath), 0700, true);
}
$serArray = serialize($accessToken);
file_put_contents($credentialsPath, $serArray);
printf("Credentials saved to %s\n", $credentialsPath);
And when you read from the file you need to unserialize it.
if (file_exists($credentialsPath)) {
$unserArray = file_get_contents($credentialsPath);
$accessToken = unserialize($unserArray);
}
Full function
function getClient() {
$client = new Google_Client();
// Set to name/location of your client_secrets.json file.
$client->setAuthConfigFile('client_secret.json');
// Set to valid redirect URI for your project.
$client->setRedirectUri('http://localhost');
$client->setApprovalPrompt('force');
$client->addScope(Google_Service_YouTube::YOUTUBE_READONLY);
$client->setAccessType('offline');
// Load previously authorized credentials from a file.
$credentialsPath = expandHomeDirectory(CREDENTIALS_PATH);
if (file_exists($credentialsPath)) {
$unserArray = file_get_contents($credentialsPath);
$accessToken = unserialize($unserArray);
} else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
print 'Enter verification code: ';
$authCode = trim(fgets(STDIN));
// Exchange authorization code for an access token.
$accessToken = $client->authenticate($authCode);
// Store the credentials to disk.
if(!file_exists(dirname($credentialsPath))) {
mkdir(dirname($credentialsPath), 0700, true);
}
$serArray = serialize($accessToken);
file_put_contents($credentialsPath, $serArray);
printf("Credentials saved to %s\n", $credentialsPath);
}
$client->setAccessToken($accessToken);
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->refreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, $client->getAccessToken());
}
return $client;
}
Google has updated their PHP Quickstart, with an improved method to handle this:
Snippet below:
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
In my case I had forgotten to set the access type as "offline" without which the refresh token was not being generated.
$client->setAccessType('offline');
Once this is done, the sample code given in the google documentation will work.
// Exchange authorization code for an access token.
// "refresh_token" is returned along with the access token
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
So After some time vieweing this code:
// Exchange authorization code for an access token.
// "refresh_token" is returned along with the access token
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
all was needed is this change:
// Exchange authorization code for an access token.
// "refresh_token" is returned along with the access token
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($accessToken);
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
since this function $client->getRefreshToken() returns null, and if you provide the $accessToken directly, it will work just fine and update your file, hope it resolves somebody issue.

Google API: refresh_token missing (access type = offline)

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

Categories