2

I want my PHP web application to create spreadsheets in a specific google account (not arbitrary user's account, but a specific account of our company).

What I've tested and does work

I have required the google/apiclient PHP client library. It is up and running.

I have those private methods in my controller:

private function getGoogleDriveService( string $scopes ) : \Google_Service_Drive
{
    $client = $this->getClient( $scopes );

    $service = new \Google_Service_Drive( $client );

    return $service;
}

private function getClient( string $scopes ) : \Google_Client
{
    $client = new \Google_Client();

    $client->setApplicationName( 'My nice application name.' );
    $client->setAuthConfig( $this->getSecretPath() );
    $client->setScopes( $scopes );
    $client->setAccessType( 'offline' );

    return $client;
}

private function getSecretPath() : string
{
    $projectDir = $this->get( 'kernel' )->getProjectDir();
    $credentialsFullPath = $projectDir . '/app/config/googleApiSecret.json';

    return $credentialsFullPath;
}

I call the getGoogleDriveService telling what scopes I want for the specific controller action, to create the Google Service. The service is created first by calling the getClient which returns an initialized \Google_Client object, which in turn gets the credentials.json path passed into it.

So far, so good. This works.

For example with a code like this:

$driveService = $this->getGoogleDriveService( \Google_Service_Drive::DRIVE );

$file = new \Google_Service_Drive_DriveFile();
$file->setName( 'My nice dummy file' );
$file->setMimeType( 'application/vnd.google-apps.spreadsheet' );
$file = $driveService->files->create( $file );

$feedbackMessage = sprintf( 'Created spreadsheet via DRIVE API with Id: %s', $file->id );

But handicap!! This is working as a "Service Account". It creates and modifies the files in some sort of "secret" place which is not visible from the Drive web frontend.

What happens

I have a user (call it for example alice@gmail.com) and this user is the one that created the "Service account". The credentials file contains some sort of "ficticious email address" more or less ressembling this: alice@my-nice-super-project.iam.gserviceaccount.com

The .json is more or less like this one:

{
  "type": "service_account",
  "project_id": "my-nice-super-project",
  "private_key_id": "7777777777777788888888888888899999999999",
  "private_key": "-----BEGIN PRIVATE KEY-----\nxxx[...]xxx==\n-----END PRIVATE KEY-----\n",
  "client_email": "alice@my-nice-super-project.iam.gserviceaccount.com",
  "client_id": "123456789123456789123",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://accounts.google.com/o/oauth2/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/alice%40my-nice-super-project.iam.gserviceaccount.com"
}

What happens is:

  1. If I login into the google drive (with a browser) with alice@gmail.com and I manually create the spreadsheet file Hello-1 then I cannot read it from the application.
  2. If I manually share the file Hello-1 from the website logged in as alice@gmail.com and sharing it to alice@my-nice-super-project.iam.gserviceaccount.com, then I can read the file from the application. But I cannot delete it from the application.
  3. If I create the file Hello-2 from the application, it's not visible from the alice@gmail.com web frontend.

It seems that even if the Service Account was created from alice@gmail.com it works as a completely separated storage and the files from the "human Alice" and the "robot Alice" are treated as from different users.

What I need

What I want is that the application can read/write the files of alice@gmail.com WITHOUT prompting for OAuth permissions.

So, to be clear: I don't want my PHP web application to be able to "edit anyone's Drive with his consent", but what I want is that "anyone can edit Alice's Drive".

The reason behind is that we already own the Alice account and it is like the "central storage" for some documents.

Multiple agents in the company must be able to edit the content via the application and only the boss will be able to login via the Google Drive site. The agents won't have Alice password so they can't consent via OAuth. But the company owns the Alice account so we can enter there and create API Keys, and set them in the server.

Where I am stuck

I can manage to make the software work from the Service Account. But this is not what I want: I want that the software works "on Alice's documents", not on an "Alice Service Account's documents" as they seem they live in separate worlds.

I don't know how to initialize the

$service = new \Google_Service_Drive( $client );

so it works with Alice's files without OAuth consent.

Xavi Montero
  • 9,239
  • 7
  • 57
  • 79
  • 1
    Although I'm not sure whether I could correctly understand about your goal, for example, how about creating a folder in the drive of `alice@gmail.com` and sharing the folder with the service account? And, when the application creates new file in the shared folder, how about changing the owner of the file to `alice@gmail.com` and sharing it with the service account? By this, the application can write and read the file of `alice@gmail.com` and `alice@gmail.com` can see the file in own account with the browser. If I misunderstood your goal, I apologize. – Tanaike Nov 17 '19 at 23:44
  • 1
    Indeed! It does work. From `alice@gmail.com` I created a folder (in this case `Quotations`) and I shared it from the web interface with `alice@my-nice-super-project.iam.gserviceaccount.com`. From then on, the application service account could use the folder. Using `$spreadsheetFile->setParents( [ $quotationsFolderId ] )` just before creating the sheet makes it appear in that folder. Please, @Tanaike, if you could set your comment as a properly formatted answer, I would be able to select it as the preferred answer and upvote it. Thanks! – Xavi Montero Nov 18 '19 at 11:47
  • Thank you for replying. I'm glad your issue was resolved. I posted it as an answer. Could you please confirm it? – Tanaike Nov 18 '19 at 22:21
  • 1
    Selected! Thanks! – Xavi Montero Nov 24 '19 at 16:16

2 Answers2

1

When you use service accounts and if you have a G Suite Account, you can use Domain-Wide Delegation of Authority, in that way your service account alice@my-nice-super-project.iam.gserviceaccount.com will be able to impersonate any user you want, in this case your alice@gmail.com.

That will help you to create files in your alice@gmail.com Drive using the code you already have. You would only need to modify this part of your code:

private function getClient( string $scopes ) : \Google_Client
{
    // Add this line of code
    // User you want to "impersonate"
    $user = "alice@gmail.com"

    $client = new \Google_Client();

    $client->setApplicationName( 'My nice application name.' );
    $client->setAuthConfig( $this->getSecretPath() );
    $client->setScopes( $scopes );
    $client->setAccessType( 'offline' );
   // Add this line of code
   $client->setSubject( $user );

    return $client;
}

HERE you can check more about the Google APIs Client Library for PHP.

alberto vielma
  • 2,302
  • 2
  • 8
  • 15
1
  • You want to write and read the files in the Google Drive of alice@gmail.com using the service account.
  • You want to write and read as the files of alice@gmail.com with the service account.
  • You want to see the files with the browser.

For achieving above situation, I would like to propose the following methods. Please think of this as just one of several possible answers.

Methods:

  1. Create a folder in the drive of alice@gmail.com and sharing the folder with the service account.
  2. When the application with the service account creates new file in the shared folder, changes the owner of the file to alice@gmail.com and sharing it with the service account.

By above methods, the application can write and read the file of alice@gmail.com and alice@gmail.com can see the file in own account with the browser.

Note:

  • The Drive of alice@gmail.com is different from the Drive of the service account. And the Drive of the service account cannot be directly seen by the browser. So in this method, the share is used.

Reference:

Tanaike
  • 181,128
  • 11
  • 97
  • 165