I'm running some Google drive project that will handles uploading of some file, I have a code "refreshtoken.php" that will do getting the authentication link and do the authentication process and will call the "callback.php" that will get the authentication code in exchange for refresh token(see the code below).
This code work fine for me, but after maybe 24 hours I need to do the authentication process again. I want this authentication process to be done only once because in my project their will be no person involve so nobody will do the authentication manually. Any help would greatly appreciated.
"refreshtoken.php"
<?php
require __DIR__ . '/vendor/autoload.php'; // load library
session_start();
$client = new Google_Client();
// Get your credentials from the console
$client->setApplicationName('Google Drive API PHP Quickstart');
$client->setRedirectUri('http://localhost/query.php');
$client->setScopes(Google_Service_Drive::DRIVE);
$client->setAuthConfig('credentials.json');
$client->setAccessType('offline');
$client->setPrompt('select_account consent');
if (isset($_GET['code'])) {
$client->authenticate($_GET['code']);
$_SESSION['token'] = $client->getAccessToken();
$client->getAccessToken(["refreshToken"]);
$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 "Created = " . $token->created . '<br/>';
echo "<a class='logout' href='?logout'>Logout</a>";
file_put_contents("token.txt",$token->refresh_token); // saving access token to file for future use
} else {
$authUrl = $client->createAuthUrl();
print "<a id ='connect' class='login' href='$authUrl'>Connect Me!</a>";
}
?>
</body>
</html>
"callback.php"
<?php
require __DIR__ . '/vendor/autoload.php';
function url_origin( $s, $use_forwarded_host = false )
{
$ssl = ( ! empty( $s['HTTPS'] ) && $s['HTTPS'] == 'on' );
$sp = strtolower( $s['SERVER_PROTOCOL'] );
$protocol = substr( $sp, 0, strpos( $sp, '/' ) ) . ( ( $ssl ) ? 's' : '' );
$port = $s['SERVER_PORT'];
$port = ( ( ! $ssl && $port=='80' ) || ( $ssl && $port=='443' ) ) ? '' : ':'.$port;
$host = ( $use_forwarded_host && isset( $s['HTTP_X_FORWARDED_HOST'] ) ) ? $s['HTTP_X_FORWARDED_HOST'] : ( isset( $s['HTTP_HOST'] ) ? $s['HTTP_HOST'] : null );
$host = isset( $host ) ? $host : $s['SERVER_NAME'] . $port;
return $protocol . '://' . $host;
}
function full_url( $s, $use_forwarded_host = false )
{
return url_origin( $s, $use_forwarded_host ) . $s['REQUEST_URI'];
}
function GetBetween($content,$start,$end)
{
$r = explode($start, $content);
if (isset($r[1])){
$r = explode($end, $r[1]);
return $r[0];
}
return '';
}
$absolute_url = full_url( $_SERVER );
$code=GetBetween($absolute_url,'code=','&');
echo "Authentication code: ".$code;
$client = new Google_Client();
$client->setApplicationName('Google Drive API PHP Quickstart');
$client->setRedirectUri('http://localhost/query.php');
$client->setScopes(Google_Service_Drive::DRIVE);
$client->setAuthConfig('credentials.json');
$client->setAccessType('offline');
$client->setPrompt('select_account consent');
$tokenPath = 'token.json';
$authCode = $code;
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
if (!file_exists(dirname($tokenPath))) {
mkdir(dirname($tokenPath), 0700, true);
}
file_put_contents($tokenPath, json_encode($client->getAccessToken()));
?>
My comments and the answer by Senthil already contains the needed code, but it seems the OP is still confused, so I will write an answer to explain the core concept.
Google Drive API is an API that authenticates via OAuth. https://en.wikipedia.org/wiki/OAuth
In simple term, it's a mechanism to allow a client app (your program / your code), to access / consume a user's (resource owner's) resource (his Google Drive storage space), that exist in Google's server (resource server and also acting as authorization server).
Quoting from wikipedia:
OAuth (Open Authorization[1][2]) is an open standard for access delegation, commonly used as a way for Internet users to grant websites or applications access to their information on other websites but without giving them the passwords.[3][4] This mechanism is used by companies such as Amazon,[5] Google, Facebook, Microsoft and Twitter to permit the users to share information about their accounts with third-party applications or websites.
As client app, the flow goes like this:
redirecting user to google's oauth authorize endpoint
receiving redirection from google's oauth, with authorization code in url parameter (authorization code = one use code, short lived)
exchanging authorization code with refresh token (refresh token = can be used multiple times, long lived)
exchanging refresh token with access token (access token = used to do the API call / to access user's resource, short lived)
consuming the API, which needs access token
Above it's said that refresh token is long lived, but for actually how long? This actually depends on specific implementation of the oauth protocol, but for Google's oauth, I have asked a question myself 4 years ago here: When will a google oauth2 refresh token expired? and until this answer is written, the refresh token generated then (4 years ago) has not expired yet. I do make sure to periodically use the refresh token (at least once a month by cron). Also, if the user manually revoked your refresh token (revoke access to your app), your refresh token will be dead.
Here is the flow:
when user authenticates, call $client->getRefreshToken(), then save the result in database, in file, or in any other persistent storage, example:
$refresh_token = $client->getRefreshToken();
file_put_contents("/home/myusername/refresh_token.txt", $refresh_token);
when you need to access user's resource / call the API (e.g.: when uploading files automatically without user's intervention), load the stored refresh token, then call the API like this:
$refresh_token = your_method_to_load_refresh_token($username);
// e.g.:
// $refresh_token = file_get_contents("/home/myusername/refresh_token.txt");
$client->fetchAccessTokenWithRefreshToken($refresh_token);
$client->some_method_to_upload_file($file);
Check whether the access token is expired with below code snippet and handle it. Based on access token you can get the refresh token accordingly or regenerate a new refresh token based on access token. Code borrowed from https://developers.google.com/drive/api/v3/quickstart/php
// 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()));
}
Related
This is the code I have and it does not work for me, it returns the error commented on the subject. I have done the whole process by registering, delegating the entire domain and authorizing everything and it fails...
$REDIRECT_URI = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
$KEY_LOCATION = $_SERVER['DOCUMENT_ROOT'].'/cada/includes/classes/google-api-php-client-2.1.1/client_secret_1050007893675-7bkmqjiqr1p57s9alr7fu7e016lrh5hc.apps.googleusercontent.com.json';
$TOKEN_FILE = $_SERVER['DOCUMENT_ROOT']."/cada/share/token.txt";
$SCOPES = array(
Google_Service_Calendar::CALENDAR
);
$client = new Google_Client();
$client->setApplicationName("SimesatSat");
$client->setAuthConfig($KEY_LOCATION);
// Incremental authorization
$client->setIncludeGrantedScopes(true);
// Allow access to Google API when the user is not present.
$client->setAccessType('offline');
$client->setRedirectUri($REDIRECT_URI);
$client->setScopes($SCOPES);
if (isset($_GET['code']) && !empty($_GET['code']))
{
try
{
// Exchange the one-time authorization code for an access token
$accessToken = $client->fetchAccessTokenWithAuthCode($_GET['code']);
// Save the access token and refresh token in local filesystem
file_put_contents($TOKEN_FILE, json_encode($accessToken));
$_SESSION['accessToken'] = $accessToken;
header('Location: ' . filter_var($REDIRECT_URI, FILTER_SANITIZE_URL));
exit();
}
catch (\Google_Service_Exception $e)
{
print_r($e);
}
}
if (!isset($_SESSION['accessToken']))
{
$token = #file_get_contents($TOKEN_FILE);
if ($token == null)
{
// Generate a URL to request access from Google's OAuth 2.0 server:
$authUrl = $client->createAuthUrl();
// Redirect the user to Google's OAuth server
header('Location: ' . filter_var($authUrl, FILTER_SANITIZE_URL));
exit();
}
else
{
$_SESSION['accessToken'] = json_decode($token, true);
}
}
$client->setAccessToken($_SESSION['accessToken']);
/* Refresh token when expired */
if ($client->isAccessTokenExpired())
{
// the new access token comes with a refresh token as well
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($TOKEN_FILE, json_encode($client->getAccessToken()));
}
////////////CONSULTA DATOS ////////////////////////////
$service = new Google_Service_Calendar($client);
$results = $service->calendarList->listCalendarList();
print_r($results);
Can anybody help me? I have another project with another client with the same code and it works perfectly! I do not understand what's happening. The only difference is that this project is part of a subdomain. Could that be the mistake?
After finally getting something besides NULL to be returned for an access token or refresh token, I am now getting an error saying my authorization token is expired. I've tried authorizing with 2 different Google accounts at different hours of the day, but I keep on getting the same authorization token.
How do I get a new, valid authorization token? When I go to account settings to remove authorization access for the app I am building in hopes to reset it, it isn't listed as an authorized app / service.
Here is my code:
require_once 'vendor/autoload.php';
$redirect_uri = 'site url';
$client = new Google_Client();
$client->setAuthConfig('client_secrets.json');
$client->setRedirectUri($redirect_uri);
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->authenticate($_GET['code']);
$refreshToken = $client->getRefreshToken();
var_dump($refreshToken);
$accessToken = $client->getAccessToken();
$aT = $client->fetchAccessTokenWithAuthCode($_GET['code']);
var_dump($accessToken);
var_dump($aT);
The last line is what returns the error. The other var_dumps return NULL.
The process you are following for authentication is not the proper one, or at least that what it seems. After setting the client configuration you have to check if you have an access code, if there is no access code then you redirect the user to authenticate and after the user is authenticated, you redirect the user to do what you want. You are also missing the scopes your app needs in order to get proper access. Delete your app from the connected apps and sites in your Google profile and then try the following please:
<?php session_start();
//INCLUDE PHP CLIENT LIBRARY
require_once 'vendor/autoload.php';
$scopes = array("email");
// Create client object
$client = new Google_Client();
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/index.php');
$client->setAuthConfig("client_secrets.json");
$client->addScope($scopes);
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
if ( isset($_SESSION['access_token']) && $_SESSION["access_token"]){
$client->setAccessToken($_SESSION['access_token']);
print_r($_SESSION['access_token']);
echo "<br>***************************************************************************************************************************************<br>";
print "Access token creation time: ".date('M d, Y - H:i:s', $_SESSION['access_token']['created']);
echo "<br>*********************************************************************<br>";
print "Refresh token: ".$_SESSION['access_token']['refresh_token'];
} else {
if (!isset($_GET['code'])) {
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
} else {
$client->authenticate($_GET['code']);
$_SESSION['access_token'] = $client->getAccessToken();
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/index.php';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
}
?>
Please don't forget to review the documentation that explains the authorization process wich is found here.
I am trying to get the organizations from a user with Google+ API or Google+ Domains API. I am following the steps on the official documentation and the logic I'm using is this one:
<?php session_start();
require_once 'vendor/autoload.php'; //INCLUDE PHP CLIENT LIBRARY
$scopes = array(
"https://www.googleapis.com/auth/plus.profiles.read",
"https://www.googleapis.com/auth/plus.me"
);
// Create client object and set its configuraitons
$client = new Google_Client();
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/');
$client->setAuthConfig("creds.json");
$client->addScope($scopes);
if( isset($_SESSION["access_token"]) ) {
$client->setAccessToken($_SESSION["access_token"]);
$service = new Google_Service_PlusDomains($client);
$me = $service->people->get('me');
var_dump($me);
echo "<br><br>*********************************************<br><br>";
$orgs = $me->getOrganizations(); // (THIS IS EMPTY!!!) ????
var_dump($orgs);
} else {
if( !isset($_GET["code"]) ){
$authUrl = $client->createAuthUrl();
header('Location: ' . filter_var($authUrl, FILTER_SANITIZE_URL));
} else {
$client->authenticate($_GET['code']);
$_SESSION['access_token'] = $client->getAccessToken();
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
}
?>
This perfectly works for G-Suite account I had before the transition of Google+ to Google+ Domains. When I use this same script on a newer G Suite account, it won't work. I have tried with $service = new Google_Service_Plus($client); and the result is the same thing. Any idea why it won't work with newer G Suite accounts? Is anybody else having the same issue?
Ok. I found the root cause of my problem. It happens that the User Resource and the People Resource are two different resources. Both of them have the "organization" attribute but the information of the user resource will not appear in your Google Plus profile and in order to populate the "organization" attribute of the people resource, the user is required to manually update the information from the "about me" page in Google Plus. At the moment, seems there is no way to programmatically update the information of the People Resource but users have to do it manually.
i am trying to create a token/refreshToken so my website will be able to submit data to Google Sheets without asking the end user for permissions and i am struggling with this for few hours now..
I tried many different codes i found on the web + Google's docs and i made some progress but i can't get it to work and i can't figure what i am missing..
At this point i get no error (neither in my logs) but also i don't get any redirect or new window for authorizing the app
<?php
session_start();
require_once('php-google-oauth/Google_Client.php');
include_once('lib/autoload.php');
include_once('php-google-oauth/auth/Google_OAuth2.php');
$CLIENT_ID = 'xxxxxxxxxxxx.apps.googleusercontent.com';
$SECRET = 'xxxxxxxxxxxxxxxxxxxxxxxx';
$REDIRECT = 'http://mywebsite.com';
$APIKEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
// $KEY = file_get_contents('php-google-oauth/client_secret.json');
$client = new Google_Client();
$client->setApplicationName("Contacts to Google Sheets");
$client->setClientId($CLIENT_ID);
$client->setClientSecret($SECRET);
$client->setRedirectUri($REDIRECT);
$client->setScopes(array('https://spreadsheets.google.com/feeds'));
$client->setAccessType('offline'); // Gets us our refresh token
// Step 1: The user has not authenticated so we give them a link to login
if (!$client->getAccessToken() && !isset($_SESSION['token'])) {
$authUrl = $client->createAuthUrl();
}
// Step 2: The user accepted your access now you need to exchange it.
if (isset($_GET['code'])) {
$client->authenticate($_GET['code']);
$_SESSION['token'] = $client->getAccessToken();
header('Location: ' . filter_var($REDIRECT, FILTER_SANITIZE_URL));
}
// Step 3: We have access we can now create our service
if (isset($_SESSION['token'])) {
$client->setAccessToken($_SESSION['token']);
$token = $client->getAccessToken();
}
echo '<script>console.log("TOKEN: '. $token .'");</script>';
?>
Thanks in advance!
I looked at my code and tried this-
// Step 1: The user has not authenticated so we give them a link to login
if (!$client->getAccessToken() && !isset($_SESSION['token'])) {
$authUrl = $client->createAuthUrl();
echo 'Click here';
}
and now i do get a token back from Google
I followed again THIS TUTORIAL to upload a file on Google Drive with php, directly from my REMOTE SERVER: so I have created new API Project from Google API Console, enabled Drive API service, requested OAuth Client ID and Client Secret, wrote them in a script, then upload it together with Google APIs Client Library for PHP folder to this http://www.MYSERVER.com/script1.php, to retrieve Auth code:
<?php
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_DriveService.php';
$drive = new Google_Client();
$drive->setClientId('XXX'); // HERE I WRITE MY Client ID
$drive->setClientSecret('XXX'); // HERE I WRITE MY Client Secret
$drive->setRedirectUri('urn:ietf:wg:oauth:2.0:oob');
$drive->setScopes(array('https://www.googleapis.com/auth/drive'));
$gdrive = new Google_DriveService($drive);
$url = $drive->createAuthUrl();
$authorizationCode = trim(fgets(STDIN));
$token = $drive->authenticate($authorizationCode);
?>
When I visit http://www.MYSERVER.com/script1.php I allow authorization and get the Auth code that I can write in a second script. Then I upload it to http://www.MYSERVER.com/script2.php, who looks like:
<?php
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_DriveService.php';
$drive = new Google_Client();
$drive->setClientId('X'); // HERE I WRITE MY Client ID
$drive->setClientSecret('X'); // HERE I WRITE MY Client Secret
$drive->setRedirectUri('urn:ietf:wg:oauth:2.0:oob');
$drive->setScopes(array('https://www.googleapis.com/auth/drive'));
$gdrive = new Google_DriveService($drive);
$_GET['code']= 'X/XXX'; // HERE I WRITE AUTH CODE RETRIEVED AFTER RUNNING REMOTE script.php
file_put_contents('token.json', $drive->authenticate());
$drive->setAccessToken(file_get_contents('token.json'));
$doc = new Google_DriveFile();
$doc->setTitle('Test Drive');
$doc->setDescription('Document');
$doc->setMimeType('text/plain');
$content = file_get_contents('drive.txt');
$output = $gdrive->files->insert($doc, array(
'data' => $content,
'mimeType' => 'text/plain',
));
print_r($output);
?>
Well, now the file drive.txt is uploaded on my Google Drive and structure of token.json file is a sort of:
{"access_token":"XXX","token_type":"Bearer","expires_in":3600,"refresh_token":"YYY","created":1365505148}
Now, as you can imagine I can call script2.php and upload file until a certain time. Finally, the point is: I don't want the token to expire, I don't want to allow authorization each time it expire (recalling script1.php): I need to call the script2.php periodically during the day, to upload my file automatically, without user interaction. So, what's the best way to automatically refresh the token forever in this context? Do I need another script? Can I add some code to script2.php? or modify the token.json file? And where can I read the time remaining before the token expire? Thanks!
You don't have to periodically ask for an access token. If you have a refresh_token, PHP client will automatically acquire a new access token for you.
In order to retrieve an refresh_token, you need to set access_type to "offline" and ask for offline access permissions:
$drive->setAccessType('offline');
Once you get a code,
$_GET['code']= 'X/XXX';
$drive->authenticate();
// persist refresh token encrypted
$refreshToken = $drive->getAccessToken()["refreshToken"];
For future requests, make sure that refreshed token is always set:
$tokens = $drive->getAccessToken();
$tokens["refreshToken"] = $refreshToken;
$drive->setAccessToken(tokens);
If you want a force access token refresh, you can do it by calling refreshToken:
$drive->refreshToken($refreshToken);
Beware, refresh_token will be returned only on the first $drive->authenticate(), you need to permanently store it. In order to get a new refresh_token, you need to revoke your existing token and start auth process again.
Offline access is explained in detail on Google's OAuth 2.0 documentation.
After messing a lot I got this to work. I am using one file/script to get the offline token and then a class to do stuff with the api:
require_once 'src/Google/autoload.php'; // load library
session_start();
$client = new Google_Client();
// Get your credentials from the console
$client->setApplicationName("Get Token");
$client->setClientId('...');
$client->setClientSecret('...');
$client->setRedirectUri('...'); // self redirect
$client->setScopes(array('https://www.googleapis.com/auth/drive.file'));
$client->setAccessType("offline");
$client->setApprovalPrompt('force');
if (isset($_GET['code'])) {
$client->authenticate($_GET['code']);
$_SESSION['token'] = $client->getAccessToken();
$client->getAccessToken(["refreshToken"]);
$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 "Created = " . $token->created . '<br/>';
echo "<a class='logout' href='?logout'>Logout</a>";
file_put_contents("token.txt",$token->refresh_token); // saving access token to file for future use
} else {
$authUrl = $client->createAuthUrl();
print "<a class='login' href='$authUrl'>Connect Me!</a>";
}
?>
</body>
</html>
You can load refresh token from file and use it as necessary for offline access:
class gdrive{
function __construct(){
require_once 'src/Google/autoload.php';
$this->client = new Google_Client();
}
function initialize(){
echo "initializing class\n";
$client = $this->client;
// credentials from google console
$client->setClientId('...');
$client->setClientSecret('...');
$client->setRedirectUri('...');
$refreshToken = file_get_contents(__DIR__ . "/token.txt"); // load previously saved token
$client->refreshToken($refreshToken);
$tokens = $client->getAccessToken();
$client->setAccessToken($tokens);
$this->doSomething(); // go do something with the api
}
}
More here: https://github.com/yannisg/Google-Drive-Uploader-PHP