Search code examples
javascriptfirebasegoogle-cloud-functionsgoogle-drive-apigoogle-api-nodejs-client

How to call Google Drive API from Cloud Function for Firebase on behalf of a user?


I want to interact with the Google's Drive API from a Cloud Function for Firebase. For authentication / authorization, I am currently relying on getClient, which I believe uses the Service Account exposed in the Cloud Function environment:

import { google } from 'googleapis';

// Within the Cloud Function body:
const auth = await google.auth.getClient({
  scopes: [
    'https://www.googleapis.com/auth/drive.file',
  ],
});
const driveAPI = google.drive({ version: 'v3', auth });

// read files, create file etc. using `driveAPI`...

The above approach works, as long as target directories / files list the email address of the service account as an editor.

However, I'd like to interact with the Drive API on behalf of another user (which I control), so that this user becomes (for example) the owner of files being created. How can I achieve this?


Solution

  • I was able to achieve calling the Drive API on behalf of another user thanks to the suggestions made by @DalmTo.

    The first step is to configure domain-wide delegation of authority in Google Workspace for the default AppEngine Service Account.

    Next, the code in my question can be extended to receive a subject with the email of the user to impersonate via the clientOptions:

    import { google } from 'googleapis';
    
    // Within the Cloud Function body:
    const auth = await google.auth.getClient({
      scopes: [
        'https://www.googleapis.com/auth/drive.file',
      ],
      clientOptions: {
        subject: '[email protected]',
      },
      keyFile: './serviceAccountKey.json',
    });
    const driveAPI = google.drive({ version: 'v3', auth });
    
    // read files, create file etc. using `driveAPI`...
    

    Now, the truly odd thing is that this only works when also passing the service account key via the keyFile option in addition. I.e., relying on the key being automatically populated (as it is in Cloud Functions for Firebase) does NOT work when also trying to impersonate a request. There are ongoing discussions of this bug on GitHub, specifically see this comment.


    To make domain-wide delegation work without having to provide the keyFile option (which will likely require you to manage the sensitive key file in some way), another option is to sign a JWT and use it to obtain an Oauth token. The approach is outlined by Google, and I found this SO answer providing a code-example very helpful.