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;
}
...
I'm trying to create a new customer and attach a G-Suite subscription to the customer, but seem to be unable to do this.
Where am I currently:
Can create a google client object
Can check if a customer already exists with that domain
Can create a customer object
Guide:
https://developers.google.com/admin-sdk/reseller/v1/codelab/end-to-end
https://developers.google.com/admin-sdk/reseller/v1/quickstart/php
Error Code:
Uncaught Google_Service_Exception:
{"error":{"errors":[{"domain":"global","reason":"insufficientPermissions","message":"Insufficient
Permission"}]
I suspected it has something to with the scope of the permission (yes really, I just said that). The thing is that I'm following the guide from google, so I'm not sure where the problem is.
Current Scope:
function get_client()
{
$OAUTH2_SCOPES = [
Google_Service_Reseller::APPS_ORDER,
Google_Service_SiteVerification::SITEVERIFICATION,
Google_Service_Directory::ADMIN_DIRECTORY_USER,
];
$client = new Google_Client();
$client->setApplicationName('test');
$client->setScopes($OAUTH2_SCOPES);
$client->setAuthConfig(__DIR__ . '/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 = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
// 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;
}
I manage to figure out the solution myself.
The problem:
insufficient Permissions
The Solution:
The token file wasn't updated, with the newly added permissions - so deleting and recreating the cred.json file - fixed the problem :)
I implemented Google spread sheet API's for a project in which i have to run cron job at midnight to update google sheet.
Because i was new to Google spread sheet API first i used Quick start example using my email, create a project, provide a sheet id, credentials.json and make token.json after run my file.
Before creating token.json, i have to run my file in cmd then copy url, paste into browser and hit that url, that url create a token which i have to paste in cmd which create token.json.
I have complete all these step above and run my project perfectly.
Now i have to deliver this project to client i have asked him to provide me a credential.json file which he provide me but now i have to create token.json file which i can't because client have different gmail id.
Now please suggest me what should i do, i have credetials.json file.
Code for google spread sheet API :
public function getClient()
{
$client = new Google_Client();
$client->setApplicationName('Google Sheets API PHP Quickstart');
$client->setScopes(Google_Service_Sheets::SPREADSHEETS);
$client->setAuthConfig('credentials.json');
$client->setAccessType('offline');
$credentialsPath = 'token.json';
if (file_exists($credentialsPath)) {
$accessToken = json_decode(file_get_contents($credentialsPath), true);
} else {
$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);
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
}
if (!file_exists(dirname($credentialsPath))) {
mkdir(dirname($credentialsPath), 0700, true);
}
file_put_contents($credentialsPath, json_encode($accessToken));
printf("Credentials saved to %s\n", $credentialsPath);
}
$client->setAccessToken($accessToken);
if ($client->isAccessTokenExpired()) {
$jsonCred = file_get_contents($credentialsPath);
$jsonArray = json_decode($jsonCred, true);
$client->fetchAccessTokenWithRefreshToken($jsonArray["refresh_token"]);
$newAccessToken = $client->getAccessToken();
$accessToken = array_merge($jsonArray, $newAccessToken);
file_put_contents($credentialsPath, json_encode($accessToken));
}
return $client;
}
I have created my service account, got the private_key and delegated domain wide authority.
Here is my code trying to authenticate with the service account but getting the same "Invalid token format error":
session_start();
include_once 'google-api-php/vendor/autoload.php';
function getClient() {
$client = new Google_Client();
$client->setApplicationName('theName');
$client->setScopes('https://www.googleapis.com/auth/admin.directory.user.readonly');
$client->setAccessType('offline');
$client->setSubject('admin#domain.com');
$client->setAuthConfig('private_key.json');
// Load previously authorized credentials from a file.
$credentialsPath = 'private_key.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));
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
// Store the credentials to disk.
if(!file_exists(dirname($credentialsPath))) {
mkdir(dirname($credentialsPath), 0700, true);
}
file_put_contents($credentialsPath, json_encode($accessToken));
printf("Credentials saved to %s\n", $credentialsPath);
}
$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()));
}
return $client;
}
And here is screenshot of what I get from $accessToken just before
$client->setAccessToken($accessToken);
with the error itself:
https://postimg.org/image/ajgan5y27/
Any help will be appreciated. Thanks!
The problem was the outdated google api documentation.
Turns out the new version for the "getClient" function needs only this to work in case anyone is having troubles:
function getClient() {
$client = new Google_Client();
$client->setAuthConfig('private_key.json');
$client->setApplicationName('theName');
$client->setScopes(Google_Service_Sheets::SPREADSHEETS_READONLY);
return $client;
}
Doesn't need to $client->setAccessToken(); at all...
Good job google... Those are the outdated and unreliable documentation pages I took this code from:
https://developers.google.com/admin-sdk/directory/v1/quickstart/php
and
https://developers.google.com/api-client-library/php/auth/service-accounts
One more thing: in case you need to play with Google Sheets, you may need to add the account service ID (xxxxxxxxxx#xxxxxxxxxxxxxx.iam.gserviceaccount.com) to the google sheet document you want to extract information from.
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.