'Google Drive API - Service Account : make a request for access token

I am a newbie in Google API. I start my project with Google Drive API and I follow the tutorial in this google cloud document.

On the Google Cloud Console - I create my project, enable the "Google Drive API". After that, I create a "Service account" and its private key, and then save the "JSON private key file" to my local server. At this process, I didn't create any "oAuth 2.0 Client IDs" or "API Keys".

This is my "JSON private key file" in my local server:

{
  "type": "service_account",
  "project_id": "coastal-scanner-338317",
  "private_key_id": "a093062296b1cbdecab6133f6b91e470bfbca0e4",
  "private_key": "-----BEGIN PRIVATE KEY-----\n*[My Private Key]*\n-----END PRIVATE KEY-----\n",
  "client_email": *[My Client Email]*,
  "client_id": *[My Client ID]*,
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/*[My Client Email]*"
}

and this is my PHP file for request an "Access Token" from google.

    //Create Service Account's JWT
    $serviceFile = './mykey.json';  //My Private key file
    $scope='https://www.googleapis.com/auth/drive';  
    
    // Get Data from Google Service Account Oauth2 json file
    $service = json_decode(file_get_contents($serviceFile));
    
    $header = '{"alg":"RS256","typ":"JWT"}';
$aud = $service->auth_uri; // 'https://oauth2.googleapis.com/token'
    $iat = time(); $exp =  $iat + 3600;
    $claimset = '{"iss":"' . $service->client_email . '","scope":"' . $scope . '","aud":"' . $aud . '","exp":' . $exp . ',"iat":' . $iat . '}';

//URL-safe Base64 header and claimset
    $hc = base64url_encode(mb_convert_encoding($header,'UTF-8')) . '.' . base64url_encode(mb_convert_encoding($claimset,'UTF-8'));
    
// Signature with Sha256withRSA follow Google spec = RS256
    $bytepkey = openssl_pkey_get_private($service->private_key);

    openssl_sign($hc,$signature,$bytepkey,'sha256');
    openssl_free_key($bytepkey);

    $assertion =  $hc . '.' . base64url_encode($signature);
    $grant_type = urlencode('urn:ietf:params:oauth:grant-type:jwt-bearer');
    $param = "grant_type={$grant_type}&assertion={$assertion}";

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $service->auth_uri);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $user_agent);
    curl_setopt($ch, CURLOPT_POST , 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS , $param);
    $result = curl_exec($ch);
    echo $result;

Above code contain 2 function for url-safe base64 like this:

function base64url_encode($data) {
  return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
function base64url_decode($data) {
  return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}

I open the php file on my browser and see the result like this:

ฺResult on my Chrome Browser

I click on the link in the picture and it bring me to a user consent page.

Does it mean my HTTP request for access token failed or else? What're the mistakes I did?



Solution 1:[1]

I see that you are trying to do this manually and i would like to recomend that you not. Creating the jws to authorize a service account is tricky. Its much easer to just use the Google php client library

My sample ServiceAccount.php

require_once __DIR__ . '/vendor/autoload.php';

// Use the developers console and download your service account
// credentials in JSON format. Place the file in this directory or
// change the key file location if necessary.
putenv('GOOGLE_APPLICATION_CREDENTIALS='.__DIR__.'/service-account.json');

/**
 * Gets the Google client refreshing auth if needed.
 * Documentation: https://developers.google.com/identity/protocols/OAuth2ServiceAccount
 * Initializes a client object.
 * @return A google client object.
 */
function getGoogleClient() {
    return getServiceAccountClient();
}


function getServiceAccountClient() {
    try {   
        // Create and configure a new client object.        
        $client = new Google_Client();
        $client->useApplicationDefaultCredentials();
        $client->addScope([YOUR SCOPES HERE]);
        return $client;
    } catch (Exception $e) {
        print "An error occurred: " . $e->getMessage();
    }
}

Really let the client library do all the heavy lifting. The Google analytics api has a nice service account example for php. Just take the authorization stuff and replace google analytics with drive. Let me know if you have any issues Google analytics quickstart

Solution 2:[2]

I am back to update my mistake.

All the code above I wrote is work, but the only problem is "I didn't have Google API Account".

After using my friend's account. It's done the job.

This is my stupid thing that disturb you kind guy. Sorry again and thanks a lot for all your help.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 DaImTo
Solution 2 noomngow