This is an article explaining the code needed to write a PHP script which generates an access token for a service account which in turn is used to list files in a team's Google Drive.
This is very different to my code for OAuth when attended: Google Authentication - OAuth 2.0 using PHP/cURL... In that this one doesn't prompt for a user. Again this script doesn't need the client libraries, composer, vendor, etc.
Why?
This took me the best part of a month to get working. It is taken from Google's documentation as well as other forums and websites that try to explain it. Do not waste your time like I did on the public key, verifying a JWT signature, or including any third-party libraries. You can do it in pure PHP and all you need is the JSON key that you generated in your Google console.
Applies to:
- Google Drive REST API v3
- Google OAuth 2.0 v4
- Google Cloud Platform IAM
- Google Suite
- PHP v5.6.35
How?
I'm going to go through each section of the code to go through the logic and highlight any changes you may need to make.
1. First: the variables are in arrays
Well mostly. Simply because we'll be working with JSON data and this encodes/decodes easily into PHP arrays. I can also output any of the variables and responses for debugging purposes. I can also unset multiple branches of variables with fewer commands than unsetting specific variables. Let's specify the output page to be in JSON format:
// set content type of this page header('Content-Type: application/json'); // init $api = array(); $access_token = '';
- // set content type of this page
- header('Content-Type: application/json');
- // init
- $api = array();
- $access_token = '';
2a. Specify the location of your private key and token file
Here you need to enter the relative or absolute path to your private key, this should be the JSON file that you generated at Google's developer console - service accounts section. It also contains the public key under the guise of a URL (x509 certificate) but we don't need it for this script. Note that the key should not be stored in a public folder that is accessible via the web but at least stored (eg. outside your web root) where this script can access it.
The same goes for the access token, store it off the web but where this script can access it (read/write).
// Location of private key on your server (JSON downloadable from Google) $api['keys']['private']['file'] = '<relative_or_absolute_path_to_your_file_key>.json'; // Location to store access token (needs be writeable) $api['jwt']['token']['file'] = '<relative_or_absolute_path_to_your_file_token>/access_token.dat';
- // Location of private key on your server (JSON downloadable from Google)
- $api['keys']['private']['file'] = '<relative_or_absolute_path_to_your_file_key>.json';
- // Location to store access token (needs be writeable)
- $api['jwt']['token']['file'] = '<relative_or_absolute_path_to_your_file_token>/access_token.dat';
2b. Specify the impersonator
Now for testing purposes you should leave this next variable as an empty string. If you leave this blank, the script will run through and connect to Google Drive using the Service Account. The script will work without being authorized, it just won't see any files other than its own.
If you specify an impersonator and you have NOT authorized this service account via the G-Suite Administrator settings, then this script will break. Once you have authorized the service account, you can then put the email of the user the service account will be impersonating.
- Browse to https://admin.google.com
- Go to Security > Show More > Advanced Settings > Manage API Client Access
- Enter the Client ID in the field Client Name (eg. 1000389324798977991)
- Enter the scope URL in the field One or More API Scopes (eg. https://www.googleapis.com/auth/drive)
- Click Authorize
3. Google Endpoints
Only change these if you need a different scope or if the APIs get upgraded.
$api['gapis']['oauth']['grant_type'] = 'urn:ietf:params:oauth:grant-type:jwt-bearer'; $api['gapis']['oauth']['token'] = 'https://www.googleapis.com/oauth2/v4/token'; $api['gapis']['drive']['scope'] = 'https://www.googleapis.com/auth/drive'; $api['gapis']['drive']['files'] = 'https://www.googleapis.com/drive/v3/files';
- $api['gapis']['oauth']['grant_type'] = 'urn:ietf:params:oauth:grant-type:jwt-bearer';
- $api['gapis']['oauth']['token'] = 'https://www.googleapis.com/oauth2/v4/token';
- $api['gapis']['drive']['scope'] = 'https://www.googleapis.com/auth/drive';
- $api['gapis']['drive']['files'] = 'https://www.googleapis.com/drive/v3/files';
4. Declare a PHP function to send requests using cURL
A standard function that is skipping the SSL checks and returns a PHP array
function send_request($url, $header, $data, $method="GET") { $ch = curl_init(); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); $output = json_decode($response, true); return $output; }
- function send_request($url, $header, $data, $method="GET") {
- $ch = curl_init();
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
- curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
- curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- $response = curl_exec($ch);
- curl_close($ch);
- $output = json_decode($response, true);
- return $output;
- }
5. Get Minutes Remaining on cached token
So usually an access token will last for 60 minutes, in this example, we are going to store the token value so it is reused until it is about to expire. Firstly, we need to check how many minutes are remaining since the access token file was last modified:
// check minutes remaining $api['jwt']['token']['minutes']=0; if(file_exists($api['jwt']['token']['file'])){ $expiry_time = filemtime($api['jwt']['token']['file']) + 3600; $diff = $expiry_time - time(); $api['jwt']['token']['minutes'] = floor($diff/60); }
- // check minutes remaining
- $api['jwt']['token']['minutes']=0;
- if(file_exists($api['jwt']['token']['file'])){
- $expiry_time = filemtime($api['jwt']['token']['file']) + 3600;
- $diff = $expiry_time - time();
- $api['jwt']['token']['minutes'] = floor($diff/60);
- }
6a. Use existing Access Token
This if else statement simply says if the token still has at least 5 minutes left, then use the one found at the file location specified earlier.
if( $api['jwt']['token']['minutes'] > 5){ $access_token = base64_decode(file_get_contents($api['jwt']['token']['file'])); }else{
- if( $api['jwt']['token']['minutes'] > 5){
- $access_token = base64_decode(file_get_contents($api['jwt']['token']['file']));
- }else{
6b. Get the contents of Google's JSON Key
This key doesn't just contain the private key we need. It is a JSON file with links to the public key, as well as info as to the project ID, the client ID, the client Email... Stuff that this script will use so:
// Get JSON file (generated by Google) contents $api['keys']['private']['contents'] = json_decode( file_get_contents( $api['keys']['private']['file'] ), true);
- // Get JSON file (generated by Google) contents
- $api['keys']['private']['contents'] = json_decode( file_get_contents( $api['keys']['private']['file'] ), true);
6c. Generate a JSON Web Token (JWT)
Now we need to generate the infamous JWT. If you've been trying to check your base64 encoded strings at JWT.io then it's hard because the timestamps, included in the encoding, change every second. You can get it verifying the signature successfully if you go get your public key, paste both keys into jwt.io (these don't change), then paste the encoded assertion.
Anyway, this is how you generate the JWT header
// Build token header. Specify algorithm $api['jwt']['header']['alg'] = 'RS256'; $api['jwt']['header']['typ'] = 'JWT';
- // Build token header. Specify algorithm
- $api['jwt']['header']['alg'] = 'RS256';
- $api['jwt']['header']['typ'] = 'JWT';
// Build token payload for a JSON string $api['jwt']['claim_set']['iss'] = $api['keys']['private']['contents']['client_email']; if($api['gdrive']['impersonator']!=""){ $api['jwt']['claim_set']['sub'] = $api['gdrive']['impersonator']; // only if service account has been authorized } $api['jwt']['claim_set']['scope'] = $api['gapis']['drive']['scope']; $api['jwt']['claim_set']['aud'] = $api['gapis']['oauth']['token']; $api['jwt']['claim_set']['exp'] = strtotime('+1 hour'); $api['jwt']['claim_set']['iat'] = strtotime('now');
- // Build token payload for a JSON string
- $api['jwt']['claim_set']['iss'] = $api['keys']['private']['contents']['client_email'];
- if($api['gdrive']['impersonator']!=""){
- $api['jwt']['claim_set']['sub'] = $api['gdrive']['impersonator'];  // only if service account has been authorized
- }
- $api['jwt']['claim_set']['scope'] = $api['gapis']['drive']['scope'];
- $api['jwt']['claim_set']['aud'] = $api['gapis']['oauth']['token'];
- $api['jwt']['claim_set']['exp'] = strtotime('+1 hour');
- $api['jwt']['claim_set']['iat'] = strtotime('now');
// Generate assertion/signature of JWT $api['jwt']['assertion'] = rtrim(strtr(base64_encode(json_encode($api['jwt']['header'])), '+/', '-_'), '='); $api['jwt']['assertion'] .= ".".rtrim(strtr(base64_encode(json_encode($api['jwt']['claim_set'])), '+/', '-_'), '='); $result = openssl_sign( $api['jwt']['assertion'], $signature, openssl_pkey_get_private($api['keys']['private']['contents']['private_key']), 'sha256'); if ($result === true){ $api['jwt']['assertion'] .= "." . rtrim(strtr(base64_encode($signature), '+/', '-_'), '='); }
- // Generate assertion/signature of JWT
- $api['jwt']['assertion'] = rtrim(strtr(base64_encode(json_encode($api['jwt']['header'])), '+/', '-_'), '=');
- $api['jwt']['assertion'] .= ".".rtrim(strtr(base64_encode(json_encode($api['jwt']['claim_set'])), '+/', '-_'), '=');
- $result = openssl_sign(
- $api['jwt']['assertion'],
- $signature,
- openssl_pkey_get_private($api['keys']['private']['contents']['private_key']),
- 'sha256');
- if ($result === true){
- $api['jwt']['assertion'] .= "." . rtrim(strtr(base64_encode($signature), '+/', '-_'), '=');
- }
7. Generate an Access Token
With the encoded JWT that we just generated, we can send a request to the Google OAuth endpoint for a new token:
// prepare token request $url = $api['gapis']['oauth']['token']; $header[] = 'Content-Type: application/x-www-form-urlencoded'; $data['grant_type'] = $api['gapis']['oauth']['grant_type']; $data['assertion'] = $api['jwt']['assertion']; // send request $api['jwt']['token']['response'] = send_request($url, $header, $data, "POST"); // check if response exists if(isset($api['jwt']['token']['response']['access_token'])){ // store in var $access_token = $api['jwt']['token']['response']['access_token']; // store in file file_put_contents($api['jwt']['token']['file'], base64_encode($access_token)); // reset minutes counter $api['jwt']['token']['minutes'] = 59; } } // end if( $api['jwt']['token']['minutes'] > 5
- // prepare token request
- $url = $api['gapis']['oauth']['token'];
- $header[] = 'Content-Type: application/x-www-form-urlencoded';
- $data['grant_type'] = $api['gapis']['oauth']['grant_type'];
- $data['assertion'] = $api['jwt']['assertion'];
- // send request
- $api['jwt']['token']['response'] = send_request($url, $header, $data, "POST");
- // check if response exists
- if(isset($api['jwt']['token']['response']['access_token'])){
- // store in var
- $access_token = $api['jwt']['token']['response']['access_token'];
- // store in file
- file_put_contents($api['jwt']['token']['file'], base64_encode($access_token));
- // reset minutes counter
- $api['jwt']['token']['minutes'] = 59;
- }
- } // end if( $api['jwt']['token']['minutes'] > 5
8. Done
That's it! You have an access token, your service account can connect directly with its Google Drive. To get a file listing, try the following:
// Get File List $url = $api['gapis']['drive']['files']; $header[] = 'Content-Type: application/x-www-form-urlencoded'; $header[] = 'Authorization: Bearer '.$access_token; $data = array(); $api['gdrive'] = send_request($url, $header, $data, "GET"); // Output JSON echo json_encode($api, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
- // Get File List
- $url = $api['gapis']['drive']['files'];
- $header[] = 'Content-Type: application/x-www-form-urlencoded';
- $header[] = 'Authorization: Bearer '.$access_token;
- $data = array();
- $api['gdrive'] = send_request($url, $header, $data, "GET");
- // Output JSON
- echo json_encode($api, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
The full script:
Just make the changes to the first few variables, as per the above instructions, to configure it...
<?php /* ------------------------------------------------------------------------------------------------ Google Drive REST API v3 using a Service Account ------------------------------------------------------------------------------------------------ Service Account Details issued by Google Cloud Platform IAM https://console.developers.google.com/iam-admin/serviceaccounts Service Account Authorization by G-Suite Administrator https://admin.google.com Google Drive API v3: https://developers.google.com/drive/api/v3/reference Google OAuth 2.0 Playground: https://developers.google.com/oauthplayground/ Google Scopes https://developers.google.com/identity/protocols/googlescopes */ // set content type of this page header('Content-Type: application/json'); // init $api = array(); $access_token = ''; // ************************************************************************************************* // EDIT THE FOLLOWING // REMINDER: Do not store key in publicly accessible web-folder but where this script can access it. // Location of private key on your server (JSON downloadable from Google) $api['keys']['private']['file'] = '<relative_or_absolute_path_to_your_file_key>.json'; // Location to store access token (needs be writeable) $api['jwt']['token']['file'] = '<relative_or_absolute_path_to_your_file_token>/access_token.dat'; // Email of the user to impersonate (leave blank until authorized) // IMPORTANT: An admin of the GSuite domain has to authorize this client id with the GDrive scope. // 1. Browse to https://admin.google.com // 2. Go to Security > Show More > Advanced Settings > Manage API Client Access // 3. Enter the Client ID in the field "Client Name" // 4. Enter the scope URL in the field "One or More API Scopes" (eg. https://www.googleapis.com/auth/drive) // 5. Click "Authorize" $api['gdrive']['impersonator'] = ""; // Google Defaults (only change if the API needs upgrading) $api['gapis']['oauth']['grant_type'] = 'urn:ietf:params:oauth:grant-type:jwt-bearer'; $api['gapis']['oauth']['token'] = 'https://www.googleapis.com/oauth2/v4/token'; $api['gapis']['drive']['scope'] = 'https://www.googleapis.com/auth/drive'; $api['gapis']['drive']['files'] = 'https://www.googleapis.com/drive/v3/files'; // ************************************************************************************************* // FUNCTION // function using PHP & cURL to send requests. Returns array function send_request($url, $header, $data, $method="GET") { $ch = curl_init(); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); $output = json_decode($response, true); return $output; } // ************************************************************************************************* // GENERATE OAUTH ACCESS TOKEN // only generate another if stored token will expire soon // check minutes remaining $api['jwt']['token']['minutes']=0; if(file_exists($api['jwt']['token']['file'])){ $expiry_time = filemtime($api['jwt']['token']['file']) + 3600; $diff = $expiry_time - time(); $api['jwt']['token']['minutes'] = floor($diff/60); } // if at least 5 minutes, then use stored token if( $api['jwt']['token']['minutes'] > 5){ $access_token = base64_decode(file_get_contents($api['jwt']['token']['file'])); }else{ // Get JSON file (generated by Google) contents $api['keys']['private']['contents'] = json_decode( file_get_contents( $api['keys']['private']['file'] ), true); // Build token header. Specify algorithm $api['jwt']['header']['alg'] = 'RS256'; $api['jwt']['header']['typ'] = 'JWT'; // Build token payload for a JSON string $api['jwt']['claim_set']['iss'] = $api['keys']['private']['contents']['client_email']; if($api['gdrive']['impersonator']!=""){ $api['jwt']['claim_set']['sub'] = $api['gdrive']['impersonator']; // only if service account has been authorized } $api['jwt']['claim_set']['scope'] = $api['gapis']['drive']['scope']; $api['jwt']['claim_set']['aud'] = $api['gapis']['oauth']['token']; $api['jwt']['claim_set']['exp'] = strtotime('+1 hour'); $api['jwt']['claim_set']['iat'] = strtotime('now'); // Generate assertion/signature of JWT $api['jwt']['assertion'] = rtrim(strtr(base64_encode(json_encode($api['jwt']['header'])), '+/', '-_'), '='); $api['jwt']['assertion'] .= ".".rtrim(strtr(base64_encode(json_encode($api['jwt']['claim_set'])), '+/', '-_'), '='); $result = openssl_sign( $api['jwt']['assertion'], $signature, openssl_pkey_get_private($api['keys']['private']['contents']['private_key']), 'sha256'); if ($result === true){ $api['jwt']['assertion'] .= "." . rtrim(strtr(base64_encode($signature), '+/', '-_'), '='); } // prepare token request $url = $api['gapis']['oauth']['token']; $header[] = 'Content-Type: application/x-www-form-urlencoded'; $data['grant_type'] = $api['gapis']['oauth']['grant_type']; $data['assertion'] = $api['jwt']['assertion']; // send request $api['jwt']['token']['response'] = send_request($url, $header, $data, "POST"); // check if response exists if(isset($api['jwt']['token']['response']['access_token'])){ // store in var $access_token = $api['jwt']['token']['response']['access_token']; // store in file file_put_contents($api['jwt']['token']['file'], base64_encode($access_token)); // reset minutes counter $api['jwt']['token']['minutes'] = 59; } } // no longer needed by this script unset($api['keys']); unset($api['jwt']); unset($api['gapis']['oauth']); // ************************************************************************************************* // Connect to GDrive and do stuff // Get File List $url = $api['gapis']['drive']['files']; $header[] = 'Content-Type: application/x-www-form-urlencoded'; $header[] = 'Authorization: Bearer '.$access_token; $data = array(); $api['gdrive'] = send_request($url, $header, $data, "GET"); // ************************************************************************************************* // OUTPUT echo json_encode($api, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
- <?php
- /*
- ------------------------------------------------------------------------------------------------
- Google Drive REST API v3 using a Service Account
- ------------------------------------------------------------------------------------------------
- Service Account Details issued by Google Cloud Platform IAM
- https://console.developers.google.com/iam-admin/serviceaccounts
- Service Account Authorization by G-Suite Administrator
- https://admin.google.com
- Google Drive API v3:
- https://developers.google.com/drive/api/v3/reference
- Google OAuth 2.0 Playground:
- https://developers.google.com/oauthplayground/
- Google Scopes
- https://developers.google.com/identity/protocols/googlescopes
- */
- // set content type of this page
- header('Content-Type: application/json');
- // init
- $api = array();
- $access_token = '';
- // *************************************************************************************************
- // EDIT THE FOLLOWING
- // REMINDER: Do not store key in publicly accessible web-folder but where this script can access it.
- // Location of private key on your server (JSON downloadable from Google)
- $api['keys']['private']['file'] = '<relative_or_absolute_path_to_your_file_key>.json';
- // Location to store access token (needs be writeable)
- $api['jwt']['token']['file'] = '<relative_or_absolute_path_to_your_file_token>/access_token.dat';
- // Email of the user to impersonate (leave blank until authorized)
- // IMPORTANT: An admin of the GSuite domain has to authorize this client id with the GDrive scope.
- // 1. Browse to https://admin.google.com
- // 2. Go to Security > Show More > Advanced Settings > Manage API Client Access
- // 3. Enter the Client ID in the field "Client Name"
- // 4. Enter the scope URL in the field "One or More API Scopes" (eg. https://www.googleapis.com/auth/drive)
- // 5. Click "Authorize"
- $api['gdrive']['impersonator'] = "";
- // Google Defaults (only change if the API needs upgrading)
- $api['gapis']['oauth']['grant_type'] = 'urn:ietf:params:oauth:grant-type:jwt-bearer';
- $api['gapis']['oauth']['token'] = 'https://www.googleapis.com/oauth2/v4/token';
- $api['gapis']['drive']['scope'] = 'https://www.googleapis.com/auth/drive';
- $api['gapis']['drive']['files'] = 'https://www.googleapis.com/drive/v3/files';
- // *************************************************************************************************
- // FUNCTION
- // function using PHP & cURL to send requests. Returns array
- function send_request($url, $header, $data, $method="GET") {
- $ch = curl_init();
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
- curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
- curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- $response = curl_exec($ch);
- curl_close($ch);
- $output = json_decode($response, true);
- return $output;
- }
- // *************************************************************************************************
- // GENERATE OAUTH ACCESS TOKEN
- // only generate another if stored token will expire soon
- // check minutes remaining
- $api['jwt']['token']['minutes']=0;
- if(file_exists($api['jwt']['token']['file'])){
- $expiry_time = filemtime($api['jwt']['token']['file']) + 3600;
- $diff = $expiry_time - time();
- $api['jwt']['token']['minutes'] = floor($diff/60);
- }
- // if at least 5 minutes, then use stored token
- if( $api['jwt']['token']['minutes'] > 5){
- $access_token = base64_decode(file_get_contents($api['jwt']['token']['file']));
- }else{
- // Get JSON file (generated by Google) contents
- $api['keys']['private']['contents'] = json_decode( file_get_contents( $api['keys']['private']['file'] ), true);
- // Build token header. Specify algorithm
- $api['jwt']['header']['alg'] = 'RS256';
- $api['jwt']['header']['typ'] = 'JWT';
- // Build token payload for a JSON string
- $api['jwt']['claim_set']['iss'] = $api['keys']['private']['contents']['client_email'];
- if($api['gdrive']['impersonator']!=""){
- $api['jwt']['claim_set']['sub'] = $api['gdrive']['impersonator'];  // only if service account has been authorized
- }
- $api['jwt']['claim_set']['scope'] = $api['gapis']['drive']['scope'];
- $api['jwt']['claim_set']['aud'] = $api['gapis']['oauth']['token'];
- $api['jwt']['claim_set']['exp'] = strtotime('+1 hour');
- $api['jwt']['claim_set']['iat'] = strtotime('now');
- // Generate assertion/signature of JWT
- $api['jwt']['assertion'] = rtrim(strtr(base64_encode(json_encode($api['jwt']['header'])), '+/', '-_'), '=');
- $api['jwt']['assertion'] .= ".".rtrim(strtr(base64_encode(json_encode($api['jwt']['claim_set'])), '+/', '-_'), '=');
- $result = openssl_sign(
- $api['jwt']['assertion'],
- $signature,
- openssl_pkey_get_private($api['keys']['private']['contents']['private_key']),
- 'sha256');
- if ($result === true){
- $api['jwt']['assertion'] .= "." . rtrim(strtr(base64_encode($signature), '+/', '-_'), '=');
- }
- // prepare token request
- $url = $api['gapis']['oauth']['token'];
- $header[] = 'Content-Type: application/x-www-form-urlencoded';
- $data['grant_type'] = $api['gapis']['oauth']['grant_type'];
- $data['assertion'] = $api['jwt']['assertion'];
- // send request
- $api['jwt']['token']['response'] = send_request($url, $header, $data, "POST");
- // check if response exists
- if(isset($api['jwt']['token']['response']['access_token'])){
- // store in var
- $access_token = $api['jwt']['token']['response']['access_token'];
- // store in file
- file_put_contents($api['jwt']['token']['file'], base64_encode($access_token));
- // reset minutes counter
- $api['jwt']['token']['minutes'] = 59;
- }
- }
- // no longer needed by this script
- unset($api['keys']);
- unset($api['jwt']);
- unset($api['gapis']['oauth']);
- // *************************************************************************************************
- // Connect to GDrive and do stuff
- // Get File List
- $url = $api['gapis']['drive']['files'];
- $header[] = 'Content-Type: application/x-www-form-urlencoded';
- $header[] = 'Authorization: Bearer '.$access_token;
- $data = array();
- $api['gdrive'] = send_request($url, $header, $data, "GET");
- // *************************************************************************************************
- // OUTPUT
- echo json_encode($api, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
Conclusions
I spent way too much time looking at libraries to encrypt using an RS256 algorithm, too much time searching for the public key and wondering why JWT.io would always invalidate my signature... The above code doesn't look like much out there on the web because nothing on the web (that I could find) works the way this script does. A testament to how the official documentation was misleading. It is written from scratch by following the logic of many other scripts rather than a clear example from start to finish.
Google Drive File Listing
The example script above will list all files the service account can see. If you want to be a bit more specific to the listing of files (by using filters), I use the following code which searches by name, folder and not trashed respectively (change the name and google folder ID as per your own configuration):
// build up query $q[] = "name='my_file.avi'"; // specify name of file to find (with extension) $q[] = "'uhIqdg8k9DcLY2p2D0A7wIRGrhg0kU2' in parents"; // specify target google folder ID here $q[] = "trashed=false"; // display items not trashed $api['gdrive']['query'] = str_replace(' ', '+', implode(' and ', $q)); // join clauses with ' and ' and replace spaces with pluses // send request to find a file in this folder (checking if file already exists) $url = 'https://www.googleapis.com/drive/v3/files?q='. $api['gdrive']['query']; $header[] = 'Content-Type: application/x-www-form-urlencoded'; $header[] = 'Authorization: Bearer '.$access_token; $data = array(); $api['gdrive']['listing'] = send_request($url, $header, $data, "GET");
- // build up query
- $q[] = "name='my_file.avi'";  // specify name of file to find (with extension)
- $q[] = "'uhIqdg8k9DcLY2p2D0A7wIRGrhg0kU2' in parents";  // specify target google folder ID here
- $q[] = "trashed=false";  // display items not trashed
- $api['gdrive']['query'] = str_replace(' ', '+', implode(' and ', $q));  // join clauses with ' and ' and replace spaces with pluses
- // send request to find a file in this folder (checking if file already exists)
- $url = 'https://www.googleapis.com/drive/v3/files?q='. $api['gdrive']['query'];
- $header[] = 'Content-Type: application/x-www-form-urlencoded';
- $header[] = 'Authorization: Bearer '.$access_token;
- $data = array();
- $api['gdrive']['listing'] = send_request($url, $header, $data, "GET");
Additional Note(s)
The script will output all the variables it needs which includes private information such as the keys, visible to anyone running the script. So my full script will delete these variables from the $api output with the following lines:
unset($api['keys']); unset($api['jwt']); unset($api['gapis']['oauth']);
- unset($api['keys']);
- unset($api['jwt']);
- unset($api['gapis']['oauth']);
I could have replaced "Bearer" with whatever the access token type was but getting this working has been a little overdue already...
If you have any suggestions or queries, feel free to comment below and I'll try to respond in kind. Hopefully this article has been helpful for others out there.
Helpful Link(s):
- Using OAuth 2.0 for Server to Server Applications
- Service Account Details issued by Google Cloud Platform IAM
- Service Account Authorization by G-Suite Administrator
- Google Scopes
- Google Drive REST API v3 Reference
- Creating and Verifying JWT Signatures in PHP using HS256 and RS256
- JWT.IO allows you to decode, verify and generate JWT
- Google OAuth 2.0 Playground