How to change the expiration time of a Google Access Token? - php

I have this code to authenticate the user using oauth 2.0 api in php but I want to change the expiry time of the token usually the token expires in 1 hour but I have to change it to the maximum time I think it is 200 days. how can I achieve that. Any help or suggestion will be appreciated
<?php
class Connection {
public function __construct() {
$this->credentials = "credentials.json";
$this->client = $this->create_client();
}
public function get_client() {
return $this->client;
}
public function get_credentials() {
return $this->credentials;
}
public function is_connected() {
return $this->is_connected;
}
public function get_unauthenticated_data() {
$authUrl = $this->client->createAuthUrl();
return "<a href='$authUrl'>Click here to link your account</a>";
}
public function credentials_in_browser() {
if ($_GET['code']) {
return true;
}
return false;
}
public function create_client() {
$client = new Google_Client();
$client->setApplicationName('Gmail API PHP');
$client->addScope('https://mail.google.com/');
$client->setAuthConfig('credentials.json');
$client->setAccessType('offline');
$client->setPrompt('select_account consent');
$tokenPath = 'token.json';
if (file_exists($tokenPath)) {
$accessToken = json_decode(file_get_contents($tokenPath), true);
$client->setAccessToken($accessToken);
}
if ($client->isAccessTokenExpired()) {
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
} elseif($this->credentials_in_browser()) {
$authCode = $_GET['code'];
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
}
} else {
$this->is_connected = false;
return $client;
}
if (!file_exists(dirname($tokenPath))) {
mkdir(dirname($tokenPath), 0700, true);
}
file_put_contents($tokenPath, json_encode($client->getAccessToken()));
}
else {}
$this->is_connected = true;
return $client;
}
}
?>

Welcome to the world of Oauth2.
Access tokens by standard expire after one hour. This is configured by the authorization server that created it. So if you own the authorization server that created it you would have access to change the expiration time.
Google Access tokens are created by Googles authorization server, Googles access tokens expire after one hour. You do not have access to change this.
That being said.
Your code appears to be using offline access and using the refresh token to request a new access token.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
Refresh tokens are long lived as long as your application is in set to production your refresh token should not expire. Your code will then request a new access token when ever it needs one.
So technically you dont need to set the access token to 200 days, your refresh token should already be longer then that.
Note with the gmail api refresh tokens will expire if the user changes their password.

Related

GMail Access Token Expired for each one hour, refresh token not working

I have a token.json file from oauth authentication to access gmail api,
{
"access_token":"token",
"expires_in":3599,
"refresh_token":"token",
"scope":"https:\/\/mail.google.com\/ https:\/\/www.googleapis.com\/auth\/gmail.compose",
"token_type":"Bearer",
"created":1615956208
}
Below I have include my code
Class Connection extends CI_Controller {
public function __construct() {
// echo .'contruct';
// die;
// parent::__construct();
$this->credentials = "assets/gmail_api/credentials/credentials.json";
$this->client = $this->create_client();
}
public function get_client() {
return $this->client;
}
public function get_credentials() {
return $this->credentials;
}
public function is_connected() {
return $this->is_connected;
}
public function get_unauthenticated_data() {
$authUrl = $this->client->createAuthUrl();
return "<a href='".$authUrl."'>Click to Link Your Gmail</a>";
}
public function credentials_in_browser() {
if (isset($_GET['code'])) {
return true;
}
return false;
}
public function create_client() {
$client = new Google_Client();
$client->setApplicationName('Gmail API PHP Quickstart');
$client->setScopes(array(
'https://mail.google.com/',
'https://www.googleapis.com/auth/gmail.compose'
));
$client->setAuthConfig($this->credentials);
$client->setAccessType('offline');
$client->setPrompt('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 = 'assets/gmail_api/'.$_SESSION['mail_box_email'].'/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());
}
elseif ($this->credentials_in_browser()) {
$authCode = $_GET['code'];
// 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));
}
}
else {
$this->is_connected = false;
return $client;
}
// Save the token to a file.
if (!file_exists(dirname($tokenPath))) {
mkdir(dirname($tokenPath), 0700, true);
}
file_put_contents($tokenPath, json_encode($client->getAccessToken()));
}
else {
$this->is_connected = true;
return $client;
}
$this->is_connected = true;
return $client;
}
}
Here i have refresh token to,
I have search about oauth access token, it always expires in 1 hour, but i want to extend this time as much long is possible, so i use refresh token, here my token.json file has refresh token, but still it's expire in one hour, i have read google oauth documentation, they said refresh token maximum life time is 200 days ( https://cloud.google.com/apigee/docs/api-platform/antipatterns/oauth-long-expiration ),
How can i increase the life time of access token, really i can't understand how it's work, please give some solution about extend the access token life time
Thank you.
Access tokens expire after an hour this is standard in all authorization servers. This is not something you can change.
What you should do is use the refresh tokens to request a new access token whenever you need one. Refresh tokens for the most part do not expire, however there are some tricks with gmail api scopes, if the user changes their password it will expire.
https://accounts.google.com/o/oauth2/token
client_id={ClientId}&client_secret={ClientSecret}&refresh_token={refreshtoken}&grant_type=refresh_token
You shouldn't need to deal with any of this if you are using the php client library all of this should be handled for you.
Oauth2Authentication.php
require_once __DIR__ . '/vendor/autoload.php';
/**
* Gets the Google client refreshing auth if needed.
* Documentation: https://developers.google.com/identity/protocols/OAuth2
* Initializes a client object.
* #return A google client object.
*/
function getGoogleClient() {
$client = getOauth2Client();
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
return $client;
}
/**
* Builds the Google client object.
* Documentation: https://developers.google.com/identity/protocols/OAuth2
* Scopes will need to be changed depending upon the API's being accessed.
* Example: array(Google_Service_Analytics::ANALYTICS_READONLY, Google_Service_Analytics::ANALYTICS)
* List of Google Scopes: https://developers.google.com/identity/protocols/googlescopes
* #return A google client object.
*/
function buildClient(){
$client = new Google_Client();
$client->setAccessType("offline"); // offline access. Will result in a refresh token
$client->setIncludeGrantedScopes(true); // incremental auth
$client->setAuthConfig(__DIR__ . '/client_secrets.json');
$client->addScope([YOUR SCOPES HERE]);
$client->setRedirectUri(getRedirectUri());
return $client;
}
/**
* Builds the redirect uri.
* Documentation: https://developers.google.com/api-client-library/python/auth/installed-app#choosingredirecturi
* Hostname and current server path are needed to redirect to oauth2callback.php
* #return A redirect uri.
*/
function getRedirectUri(){
//Building Redirect URI
$url = $_SERVER['REQUEST_URI']; //returns the current URL
if(strrpos($url, '?') > 0)
$url = substr($url, 0, strrpos($url, '?') ); // Removing any parameters.
$folder = substr($url, 0, strrpos($url, '/') ); // Removeing current file.
return (isset($_SERVER['HTTPS']) ? "https" : "http") . '://' . $_SERVER['HTTP_HOST'] . $folder. '/oauth2callback.php';
}
/**
* Authenticating to Google using Oauth2
* Documentation: https://developers.google.com/identity/protocols/OAuth2
* Returns a Google client with refresh token and access tokens set.
* If not authencated then we will redirect to request authencation.
* #return A google client object.
*/
function getOauth2Client() {
try {
$client = buildClient();
// Set the refresh token on the client.
if (isset($_SESSION['refresh_token']) && $_SESSION['refresh_token']) {
$client->refreshToken($_SESSION['refresh_token']);
}
// If the user has already authorized this app then get an access token
// else redirect to ask the user to authorize access to Google Analytics.
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
// Set the access token on the client.
$client->setAccessToken($_SESSION['access_token']);
// Refresh the access token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
$client->setAccessToken($client->getAccessToken());
$_SESSION['access_token'] = $client->getAccessToken();
}
return $client;
} else {
// We do not have access request access.
header('Location: ' . filter_var( $client->getRedirectUri(), FILTER_SANITIZE_URL));
}
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
}
?>
oauth2callback.php
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/Oauth2Authentication.php';
// Start a session to persist credentials.
session_start();
// Handle authorization flow from the server.
if (! isset($_GET['code'])) {
$client = buildClient();
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
} else {
$client = buildClient();
$client->authenticate($_GET['code']); // Exchange the authencation code for a refresh token and access token.
// Add access token and refresh token to seession.
$_SESSION['access_token'] = $client->getAccessToken();
$_SESSION['refresh_token'] = $client->getRefreshToken();
//Redirect back to main script
$redirect_uri = str_replace("oauth2callback.php",$_SESSION['mainScript'],$client->getRedirectUri());
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
?>

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;

How to fetch gmail in php without using imap?

I have used imap for fetching gmail using php. Is there any other way to fetch gmails using php without using imap?
You can use the gmail api to access a users gmail account. You will need to authenticate the user using Oauth2 rather than using the login and password you are probably using on the imap server.
Google has a quick start which can be found here
<?php
require __DIR__ . '/vendor/autoload.php';
if (php_sapi_name() != 'cli') {
throw new Exception('This application must be run on the command line.');
}
/**
* Returns an authorized API client.
* #return Google_Client the authorized client object
*/
function getClient()
{
$client = new Google_Client();
$client->setApplicationName('Gmail API PHP Quickstart');
$client->setScopes(Google_Service_Gmail::GMAIL_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;
}
// Get the API client and construct the service object.
$client = getClient();
$service = new Google_Service_Gmail($client);
// Print the labels in the user's account.
$user = 'me';
$results = $service->users_labels->listUsersLabels($user);
if (count($results->getLabels()) == 0) {
print "No labels found.\n";
} else {
print "Labels:\n";
foreach ($results->getLabels() as $label) {
printf("- %s\n", $label->getName());
}
}

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