Following code is working fine with the first access token but after I refresh the access token with refresh token, this throws UNAUTHENTICATED error.
$client = new GoogleClient();
$client->setClientId('XXXXXX');
$client->setClientSecret('XXXXX');
$client->setAccessToken('ya20.xxxxxx);
$client->refreshToken(1//xxxxxxx);
$calendarId = 'primary';
$optParams = array(
'maxResults' => 10,
'orderBy' => 'startTime',
'singleEvents' => TRUE,
'timeMin' => date('c'),
);
$calendarService = new Calendar($client);
$results = $calendarService->events->listEvents($calendarId, $optParams);
I am updating the access token with refresh token in the following way -
$client = new GoogleClient();
$client->setClientId('XXXXXX');
$client->setClientSecret('XXXXX');
$client->setAccessToken('ya20.xxxxxx);
$newToken = [];
$client->fetchAccessTokenWithRefreshToken('1//xxxx');
$newTokens = $client->getAccessToken();
if (isset($newTokens['access_token'])) {
$accessToken = $newTokens['access_token'];
$newToken['token'] = $accessToken;
}
if (isset($accessToken['refresh_token'])) {
$refreshToken = $newTokens['refresh_token'];
$newToken['refresh_token'] = $refreshToken;
}
This is returning the access token but after using this token in the above code I am getting 401 UNAUTHENTICATED Error.
I am fetching access token and refresh token with Laravel/Socialite package
$scopes = [
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/calendar',
];
return Socialite::driver('google')
->scopes($scopes)
->with([
"access_type" => "offline",
"prompt" => "consent select_account"])
->redirect();
I would question wither or not your refresh token is valid.
This is the method i use.
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();
}
}
validate refresh token
To use a refresh token is a HTTP Post call. If the call isnt working with php it would sugest to me that this is not a valid.
If You want to try and validate it directly using something like postman the call would be like this.
HTTP POST https://accounts.google.com/o/oauth2/token
client_id={ClientId}&client_secret={ClientSecret}&refresh_token=1/ffYmfI0sjR54Ft9oupubLzrJhD1hZS5tWQcyAvNECCA&grant_type=refresh_token
I've been block for two days facing 0auth problems after 1 hour on Youtube api. 401 credential error.
$OAUTH2_CLIENT_ID = 'XXXXX';
$OAUTH2_CLIENT_SECRET = 'XXXXX';
$client = new Google_Client();
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->setClientId($OAUTH2_CLIENT_ID);
$client->setClientSecret($OAUTH2_CLIENT_SECRET);
$client->setScopes('https://www.googleapis.com/auth/youtube');
$redirect = filter_var('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'],
FILTER_SANITIZE_URL);
$client->setRedirectUri($redirect);
$service = new Google_Service_YouTube($client);
if (isset($_GET['code'])){
$accessToken = $client->fetchAccessTokenWithAuthCode($_GET['code']);
$client->setAccessToken($accessToken);
$test=$client->getAccessToken();
//TEST REFRESH TOKEN
print_r($test);
sleep(10);
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
$test=$client->getAccessToken();
print_r($test);
}
// Check to ensure that the access token was successfully acquired.
if ($client->getAccessToken()) {
...
foreach ($files as $file){
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
} // end if token expired
====CALL YOUTUBE API HERE IN FOREACH LOOP ===
}// end foreach files
}
The result of my test code show me the access token didn't change at all after provided refresh token, even the expiration time have not dicreased 'expires_in'
So that's why I'm facing a credential error after an hour ... I don't know what's wrong with my code, please help me.
This the result of my test code after getting access code, so as you can see the 'new' access token is similar at the previous one and I already tried to use encode_json too on parameter of setAccessToken() and fetchAccessTokenWithRefreshToken(). Not getting error but result still same ...
Array ( [access_token] => ya29.GlvQBuGBfZDQn3E8HWd4wfSbb0hLHsYVGzPBE0boJuB4ien5pcsOGqXlkEyOU7mevDLOGOWbuakTyTiAUVf2bkxNwZXX [expires_in] => 3600 [refresh_token] => 1/KEgjy2t9kTNwCXk-ZtMTSzPSS2xl4XX [scope] => https://www.googleapis.com/auth/youtube [token_type] => Bearer [created] => 1552891085 )
Array ( [access_token] => ya29.GlvQBuGBfZDQn3E8HWd4wfSbb0hLHsYVGzPBE0boJuB4ien5pcsOGqXlkEyOU7mevDLOGOWbuakTyTiAUVf2bkxNwZXX [expires_in] => 3600 [refresh_token] => 1/KEgjy2t9kTNwCXk-ZtMTSzPSS2xl4XX [scope] => https://www.googleapis.com/auth/youtube [token_type] => Bearer [created] => 1552891085 )
Thanks you
Access tokens expire after one hour this is how they work. Once the access token has expired you should run your code and fetch a new access token. Fetching a new access token before it expires will result in the same access token.
Your access token was created at created (tip epoch converter) add 3600 (seconds) to find out when it expires.
1552891085 <--- Monday, March 18, 2019 6:38:05 AM
The only thing i can see wrong with your code is Your fetching the access token but not actually using it Oauth2Authentication.php
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();
}
}
I am a bit confused on the Access token. I have written a PHP script which inserts the data I get from the POST request , I already have authorized the App and it does add the Row at the end of the Sheet.
My Question is how I refresh the token when it get implemented on the server, as it will add the POST data.
Here is the Code
<?php
// Load the Google API PHP Client Library.
require_once __DIR__ . '/vendor/autoload.php';
session_start();
$client = new Google_Client();
$client->setAuthConfigFile(__DIR__ . '/client_secrets.json');
$client->addScope(Google_Service_Sheets::SPREADSHEETS);
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
$lead = array (
"first_name" => $_POST['name'],
"email" => $_POST['email']
);
$sid = "sheet id on which the row is added";
addRowToSpreadsheet($lead, $client , $sid);
} else {
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/fb/oauth2callback.php';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
function addRowToSpreadsheet($ary_values = array(), $client , $sid) {
$sheet_service = new Google_Service_Sheets($client);
$fileId = $sid;
$values = array();
foreach( $ary_values AS $d ) {
$cellData = new Google_Service_Sheets_CellData();
$value = new Google_Service_Sheets_ExtendedValue();
$value->setStringValue($d);
$cellData->setUserEnteredValue($value);
$values[] = $cellData;
}
// Build the RowData
$rowData = new Google_Service_Sheets_RowData();
$rowData->setValues($values);
// Prepare the request
$append_request = new Google_Service_Sheets_AppendCellsRequest();
$append_request->setSheetId(0);
$append_request->setRows($rowData);
$append_request->setFields('userEnteredValue');
// Set the request
$request = new Google_Service_Sheets_Request();
$request->setAppendCells($append_request);
// Add the request to the requests array
$requests = array();
$requests[] = $request;
// Prepare the update
$batchUpdateRequest = new Google_Service_Sheets_BatchUpdateSpreadsheetRequest(array(
'requests' => $requests
));
try {
// Execute the request
$response = $sheet_service->spreadsheets->batchUpdate($fileId, $batchUpdateRequest);
if( $response->valid() ) {
// Success, the row has been added
return true;
}
} catch (Exception $e) {
// Something went wrong
error_log($e->getMessage());
}
return false;
}
?>
I have tried hosting the app on the server and it doesn't add a new row in the Sheet, I think this is a problem due to the Access Token
Please Help
There is actually a PHP Quickstart for Sheets API which includes how to refresh tokens. Here's a snippet:
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
There's also a Refreshing an access token (offline access) guide with regard to refresh tokens
Access tokens periodically expire. You can refresh an access token
without prompting the user for permission (including when the user is
not present) if you requested offline access to the scopes associated
with the token.
If you use a Google API Client Library, the client object refreshes
the access token as needed as long as you configure that object for
offline access. If you are not using a client library, you need to set
the access_type HTTP query parameter to offline when redirecting the
user to Google's OAuth 2.0 server. In that case, Google's
authorization server returns a refresh token when you exchange an
authorization code for an access token. Then, if the access token
expires (or at any other time), you can use a refresh token to obtain
a new access token.
If your application needs offline access to a Google API, set the API
client's access type to offline:
$client->setAccessType("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 ...
I've been playing around with the Google Analytics API (V3) and have run into som errors. Firstly, everything is set up correct and worked with my testing account. But when I want to grab data from another profile ID (Same Google Accont/GA Account) I get an 403 Error. The strange thing is that data from some GA accounts will return data whilst other generate this error.
I've revoked the token and authenticated one more time, and now it seems like I can grab data from all of my accounts. Problem solved? Not. As the access key will expire, I will run into the same issue again.
If I have understood things right, one could use the resfreshToken to get a new authenticationTooken.
The problem is, when I run:
$client->refreshToken(refresh_token_key)
the following error is returned:
Error refreshing the OAuth2 token, message: '{ "error" : "invalid_grant" }'
I’ve checked the code behind the refreshToken method and tracked the request back to the “apiOAuth2.php” file. All parameters are sent correctly. The grant_type is hard coded to ‘refresh_token’ within the method, so it’s hard for me to understand what’s wrong. The parameter array looks like this:
Array ( [client_id] => *******-uqgau8uo1l96bd09eurdub26c9ftr2io.apps.googleusercontent.com [client_secret] => ******** [refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY [grant_type] => refresh_token )
The procedure is as follows.
$client = new apiClient();
$client->setClientId($config['oauth2_client_id']);
$client->setClientSecret($config['oauth2_client_secret']);
$client->setRedirectUri($config['oauth2_redirect_uri']);
$client->setScopes('https://www.googleapis.com/auth/analytics.readonly');
$client->setState('offline');
$client->setAccessToken($config['token']); // The access JSON object.
$client->refreshToken($config['refreshToken']); // Will return error here
Is this a bug, or have I completely misunderstood something?
So i finally figured out how to do this. The basic idea is that you have the token you get the first time you ask for authentication. This first token has a refresh token. The first original token expires after an hour. After an hour you have to use the refresh token from the first token to get a new usable token. You use $client->refreshToken($refreshToken) to retrieve a new token. I will call this "temp token." You need to store this temp token as well because after an hour it expires as well and note it does not have a refresh token associated with it. In order to get a new temp token you need to use the method you used before and use the first token's refreshtoken. I have attached code below, which is ugly, but im new at this...
//pull token from database
$tokenquery="SELECT * FROM token WHERE type='original'";
$tokenresult = mysqli_query($cxn,$tokenquery);
if($tokenresult!=0)
{
$tokenrow=mysqli_fetch_array($tokenresult);
extract($tokenrow);
}
$time_created = json_decode($token)->created;
$t=time();
$timediff=$t-$time_created;
echo $timediff."<br>";
$refreshToken= json_decode($token)->refresh_token;
//start google client note:
$client = new Google_Client();
$client->setApplicationName('');
$client->setScopes(array());
$client->setClientId('');
$client->setClientSecret('');
$client->setRedirectUri('');
$client->setAccessType('offline');
$client->setDeveloperKey('');
//resets token if expired
if(($timediff>3600)&&($token!=''))
{
echo $refreshToken."</br>";
$refreshquery="SELECT * FROM token WHERE type='refresh'";
$refreshresult = mysqli_query($cxn,$refreshquery);
//if a refresh token is in there...
if($refreshresult!=0)
{
$refreshrow=mysqli_fetch_array($refreshresult);
extract($refreshrow);
$refresh_created = json_decode($token)->created;
$refreshtimediff=$t-$refresh_created;
echo "Refresh Time Diff: ".$refreshtimediff."</br>";
//if refresh token is expired
if($refreshtimediff>3600)
{
$client->refreshToken($refreshToken);
$newtoken=$client->getAccessToken();
echo $newtoken."</br>";
$tokenupdate="UPDATE token SET token='$newtoken' WHERE type='refresh'";
mysqli_query($cxn,$tokenupdate);
$token=$newtoken;
echo "refreshed again";
}
//if the refresh token hasn't expired, set token as the refresh token
else
{
$client->setAccessToken($token);
echo "use refreshed token but not time yet";
}
}
//if a refresh token isn't in there...
else
{
$client->refreshToken($refreshToken);
$newtoken=$client->getAccessToken();
echo $newtoken."</br>";
$tokenupdate="INSERT INTO token (type,token) VALUES ('refresh','$newtoken')";
mysqli_query($cxn,$tokenupdate);
$token=$newtoken;
echo "refreshed for first time";
}
}
//if token is still good.
if(($timediff<3600)&&($token!=''))
{
$client->setAccessToken($token);
}
$service = new Google_DfareportingService($client);
The problem is in the refresh token:
[refresh_token] => 1\/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
When a string with a '/' gets json encoded, It is escaped with a '\', hence you need to remove it.
The refresh token in your case should be:
1/lov250YQTMCC9LRQbE6yMv-FiX_Offo79UXimV8kvwY
What i'm assuming you've done is that you've printed the json string which google sent back and copied and pasted the token into your code because if you json_decode it then it will correctly remove the '\' for you!
here is the snippet to set token, before that make sure the access type should be set to offline
if (isset($_GET['code'])) {
$client->authenticate();
$_SESSION['access_token'] = $client->getAccessToken();
}
To refresh token
$google_token= json_decode($_SESSION['access_token']);
$client->refreshToken($google_token->refresh_token);
this will refresh your token, you have to update it in session for that you can do
$_SESSION['access_token']= $client->getAccessToken()
The access type should be set to offline. state is a variable you set for your own use, not the API's use.
Make sure you have the latest version of the client library and add:
$client->setAccessType('offline');
See Forming the URL for an explanation of the parameters.
The answer posted by #uri-weg worked for me but as I did not find his explanations very clear, let me reword it a little.
During the first access permission sequence, in the callback, when you get to the point where you receive an authentication code, you must save the access token and the refresh token as well.
The reason is google api sends you an access token with a refresh token only when prompting for access permission. The next access tokens will be sent without any refresh token (unless you use the approval_prompt=force option).
The refresh token you received the first time stays valid until the user revokes access permission.
In simplistic php, an example of the callback sequence would be:
// init client
// ...
$authCode = $_GET['code'];
$accessToken = $client->authenticate($authCode);
// $accessToken needs to be serialized as json
$this->saveAccessToken(json_encode($accessToken));
$this->saveRefreshToken($accessToken['refresh_token']);
And later on, in simplistic php, the connection sequence would be:
// init client
// ...
$accessToken = $this->loadAccessToken();
// setAccessToken() expects json
$client->setAccessToken($accessToken);
if ($client->isAccessTokenExpired()) {
// reuse the same refresh token
$client->refreshToken($this->loadRefreshToken());
// save the new access token (which comes without any refresh token)
$this->saveAccessToken($client->getAccessToken());
}
Here is the code which I am using in my project and it is working fine:
public function getClient(){
$client = new Google_Client();
$client->setApplicationName(APPNAME); // app name
$client->setClientId(CLIENTID); // client id
$client->setClientSecret(CLIENTSECRET); // client secret
$client->setRedirectUri(REDIRECT_URI); // redirect uri
$client->setApprovalPrompt('auto');
$client->setAccessType('offline'); // generates refresh token
$token = $_COOKIE['ACCESSTOKEN']; // fetch from cookie
// if token is present in cookie
if($token){
// use the same token
$client->setAccessToken($token);
}
// this line gets the new token if the cookie token was not present
// otherwise, the same cookie token
$token = $client->getAccessToken();
if($client->isAccessTokenExpired()){ // if token expired
$refreshToken = json_decode($token)->refresh_token;
// refresh the token
$client->refreshToken($refreshToken);
}
return $client;
}
Had the same issue; my script that worked yesterday, for some odd reason did not today. No changes.
Apparently this was because my system clock was off by 2.5 (!!) seconds, syncing with NTP fixed it.
See also: https://code.google.com/p/google-api-php-client/wiki/OAuth2#Solving_invalid_grant_errors
Sometimes Refresh Token i not generated by using $client->setAccessType ("offline");.
Try this:
$client->setAccessType ("offline");
$client->setApprovalPrompt ("force");
FYI: The 3.0 Google Analytics API will automatically refresh the access token if you have a refresh token when it expires so your script never needs refreshToken.
(See the Sign function in auth/apiOAuth2.php)
You need to save the access token to file or database as a json string during the initial authorization request, and set the access type to offline $client->setAccessType("offline")
Then, during subsequent api requests, grab the access token from your file or db and pass it to the client:
$accessToken = json_decode($row['token'], true);
$client->setAccessToken($accessToken);
Now you need to check if the token has expired:
if ($client->isAccessTokenExpired()) {
// access token has expired, use the refresh token to obtain a new one
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
// save the new token to file or db
// ...json_encode($client->getAccessToken())
The fetchAccessTokenWithRefreshToken() function will do the work for you and provide a new access token, save it back to your file or database.
I used the example by smartcodes with the current version of the Google API, but that one didn't work. I think his API is too outdated.
So, I just wrote my own version, based on one of the API examples... It outputs access token, request token, token type, ID token, expiration time and creation time as strings
If your client credentials and developer key are correct, this code should work out of the box.
<?php
// Call set_include_path() as needed to point to your client library.
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_Oauth2Service.php';
session_start();
$client = new Google_Client();
$client->setApplicationName("Get Token");
// Visit https://code.google.com/apis/console?api=plus to generate your
// oauth2_client_id, oauth2_client_secret, and to register your oauth2_redirect_uri.
$oauth2 = new Google_Oauth2Service($client);
if (isset($_GET['code'])) {
$client->authenticate($_GET['code']);
$_SESSION['token'] = $client->getAccessToken();
$redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
return;
}
if (isset($_SESSION['token'])) {
$client->setAccessToken($_SESSION['token']);
}
if (isset($_REQUEST['logout'])) {
unset($_SESSION['token']);
$client->revokeToken();
}
?>
<!doctype html>
<html>
<head><meta charset="utf-8"></head>
<body>
<header><h1>Get Token</h1></header>
<?php
if ($client->getAccessToken()) {
$_SESSION['token'] = $client->getAccessToken();
$token = json_decode($_SESSION['token']);
echo "Access Token = " . $token->access_token . '<br/>';
echo "Refresh Token = " . $token->refresh_token . '<br/>';
echo "Token type = " . $token->token_type . '<br/>';
echo "Expires in = " . $token->expires_in . '<br/>';
echo "ID Token = " . $token->id_token . '<br/>';
echo "Created = " . $token->created . '<br/>';
echo "<a class='logout' href='?logout'>Logout</a>";
} else {
$authUrl = $client->createAuthUrl();
print "<a class='login' href='$authUrl'>Connect Me!</a>";
}
?>
</body>
</html>
Google has made some changes since this question was originally posted.
Here is my currently working example.
public function update_token($token){
try {
$client = new Google_Client();
$client->setAccessType("offline");
$client->setAuthConfig(APPPATH . 'vendor' . DIRECTORY_SEPARATOR . 'google' . DIRECTORY_SEPARATOR . 'client_secrets.json');
$client->setIncludeGrantedScopes(true);
$client->addScope(Google_Service_Calendar::CALENDAR);
$client->setAccessToken($token);
if ($client->isAccessTokenExpired()) {
$refresh_token = $client->getRefreshToken();
if(!empty($refresh_token)){
$client->fetchAccessTokenWithRefreshToken($refresh_token);
$token = $client->getAccessToken();
$token['refresh_token'] = json_decode($refresh_token);
$token = json_encode($token);
}
}
return $token;
} catch (Exception $e) {
$error = json_decode($e->getMessage());
if(isset($error->error->message)){
log_message('error', $error->error->message);
}
}
}
I have a same problem with google/google-api-php-client v2.0.0-RC7
and after search for 1 hours, i solved this problem using json_encode
like this:
if ($client->isAccessTokenExpired()) {
$newToken = json_decode(json_encode($client->getAccessToken()));
$client->refreshToken($newToken->refresh_token);
file_put_contents(storage_path('app/client_id.txt'), json_encode($client->getAccessToken()));
}
This here works very good, maybe it could help anybody:
index.php
session_start();
require_once __DIR__.'/client.php';
if(!isset($obj->error) && isset($_SESSION['access_token']) && $_SESSION['access_token'] && isset($obj->expires_in)) {
?>
<!DOCTYPE html>
<html>
<head>
<title>Google API Token Test</title>
<meta charset='utf-8' />
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script>
search('Music Mix 2010');
function search(q) {
$.ajax({
type: 'GET',
url: 'action.php?q='+q,
success: function(data) {
if(data == 'refresh') location.reload();
else $('#response').html(JSON.stringify(JSON.parse(data)));
}
});
}
</script>
</head>
<body>
<div id="response"></div>
</body>
</html>
<?php
}
else header('Location: '.filter_var('https://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']).'/oauth2callback.php', FILTER_SANITIZE_URL));
?>
oauth2callback.php
require_once __DIR__.'/vendor/autoload.php';
session_start();
$client = new Google_Client();
$client->setAuthConfigFile('auth.json');
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->setRedirectUri('https://'.filter_var($_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'], FILTER_SANITIZE_URL));
$client->addScope(Google_Service_YouTube::YOUTUBE_FORCE_SSL);
if(isset($_GET['code']) && $_GET['code']) {
$client->authenticate(filter_var($_GET['code'], FILTER_SANITIZE_STRING));
$_SESSION['access_token'] = $client->getAccessToken();
$_SESSION['refresh_token'] = $_SESSION['access_token']['refresh_token'];
setcookie('refresh_token', $_SESSION['refresh_token'], time()+60*60*24*180, '/', filter_var($_SERVER['HTTP_HOST'], FILTER_SANITIZE_URL), true, true);
header('Location: '.filter_var('https://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']), FILTER_SANITIZE_URL));
exit();
}
else header('Location: '.filter_var($client->createAuthUrl(), FILTER_SANITIZE_URL));
exit();
?>
client.php
// https://developers.google.com/api-client-library/php/start/installation
require_once __DIR__.'/vendor/autoload.php';
$client = new Google_Client();
$client->setAuthConfig('auth.json');
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->addScope(Google_Service_YouTube::YOUTUBE_FORCE_SSL);
// Delete Cookie Token
#setcookie('refresh_token', #$_SESSION['refresh_token'], time()-1, '/', filter_var($_SERVER['HTTP_HOST'], FILTER_SANITIZE_URL), true, true);
// Delete Session Token
#unset($_SESSION['refresh_token']);
if(isset($_SESSION['refresh_token']) && $_SESSION['refresh_token']) {
$client->refreshToken($_SESSION['refresh_token']);
$_SESSION['access_token'] = $client->getAccessToken();
}
elseif(isset($_COOKIE['refresh_token']) && $_COOKIE['refresh_token']) {
$client->refreshToken($_COOKIE['refresh_token']);
$_SESSION['access_token'] = $client->getAccessToken();
}
$url = 'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token='.urlencode(#$_SESSION['access_token']['access_token']);
$curl_handle = curl_init();
curl_setopt($curl_handle, CURLOPT_URL, $url);
curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 2);
curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl_handle, CURLOPT_USERAGENT, 'Google API Token Test');
$json = curl_exec($curl_handle);
curl_close($curl_handle);
$obj = json_decode($json);
?>
action.php
session_start();
require_once __DIR__.'/client.php';
if(isset($obj->error)) {
echo 'refresh';
exit();
}
elseif(isset($_SESSION['access_token']) && $_SESSION['access_token'] && isset($obj->expires_in) && isset($_GET['q']) && !empty($_GET['q'])) {
$client->setAccessToken($_SESSION['access_token']);
$service = new Google_Service_YouTube($client);
$response = $service->search->listSearch('snippet', array('q' => filter_input(INPUT_GET, 'q', FILTER_SANITIZE_SPECIAL_CHARS), 'maxResults' => '1', 'type' => 'video'));
echo json_encode($response['modelData']);
exit();
}
?>
I use google-api-php-client v2.2.2 I get a new token with fetchAccessTokenWithRefreshToken(); if function call without params, it returns an updated access token and the refreshed token is not lost.
if ($client->getAccessToken() && $client->isAccessTokenExpired()) {
$new_token=$client->fetchAccessTokenWithRefreshToken();
$token_data = $client->verifyIdToken();
}
use the following code snippet to get your refresh token
<?php
require_once 'src/apiClient.php';
require_once 'src/contrib/apiTasksService.php';
$client = new apiClient();
$client->setAccessType('offline');
$tasksService = new apiTasksService($client);
$auth = $client->authenticate();
$token = $client->getAccessToken();
// the refresh token
$refresh_token = $token['refresh_token'];
?>
According to Authentication on google: OAuth2 keeps returning 'invalid_grant'
"You should reuse the access token you get after the first successful authentication. You will get an invalid_grant error if your previous token has not expired yet. Cache it somewhere so you can reuse it."
hope it helps
I got into this issue and I found this to be the simplest and cleanest way to get proper token.
public function authenticate()
{
$access_token = 'OLD_TOKEN';
$refresh_token = 'OLD_TOKEN';
if ($access_token) {
$this->client->setAccessToken($access_token);
}
if ($this->client->isAccessTokenExpired()) {
$this->client->refreshToken($refresh_token);
}
}
I have client as property on the class that's why I am using $this->client.