0

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()));

?>
  • you can just save the refresh token somewhere (e.g.: in variable, in database, etc), then use it to generate new access token whenever you want to upload – Kristian Oct 27 '21 at 06:03
  • if you only need to upload to one user's drive (e.g.: your own drive only), you can try to mount google drive as FUSE (e.g.: with rclone/gcfs) and then you just need to copy your file to the mountpoint – Kristian Oct 27 '21 at 06:05
  • Thanks, can you give me sample of the code using the refresh token to generate the access token, then save it to Json file. I greatly appreciate your help. – Junry Buenavista Oct 27 '21 at 11:06

2 Answers2

1

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);
Kristian
  • 2,456
  • 8
  • 23
  • 23
  • 1
    Thanks Kristian that's a great explanation. Now it's more clear to me how google Oauth2 and the refresh token works. I'm now testing some of my codes I will get back here if I'm having some issues. Again thanks for your help! – Junry Buenavista Oct 28 '21 at 03:00
0

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()));
    }
Zoe
  • 27,060
  • 21
  • 118
  • 148
Senthil
  • 2,156
  • 1
  • 14
  • 19
  • Thanks, for your answer but I already did this. As I mention above their will be no human involve for the authentication process, once the token expired no one will will authenticate it again. That is why I want the process required for the authentication will be only once. I wonder if their is some other way. Anyway I'm still looking for answer, thanks again. – Junry Buenavista Oct 27 '21 at 10:07
  • Refresh Token, always have a validity . You can do some settings to increase the validity life time. But for security reasons, its not recommended, when its expired - request for new one – Senthil Oct 27 '21 at 12:08
  • Thanks for response, I just want to know how to increase validity of refresh token in life time, like it will never be expired since the security reason will not be an issue for us because the owner of third party app is also the owner of google drive. – Junry Buenavista Oct 28 '21 at 02:43