I am creating a service to watch some of my Shared Drive folders on Google Drive. I am using a service account for authentication as this is a service running on a server. Here is my code snippet:
const { google } = require("googleapis");
const auth = new google.auth.GoogleAuth({
keyFile: "./credentials.json",
scopes: ["https://www.googleapis.com/auth/drive"],
});
const drive = google.drive({ version: "v3", auth });
drive.drives
.list({ q: "name = Clients", useDomainAdminAccess: true })
.then((files) => {
console.log(files);
})
.catch((err) => {
console.log(err);
});
If I leave the list
method parameters empty, I get back a response with status code 200 and an empty list of drives as expected (as the service account does not own any shared drives). As soon as I add the use useDomainAdminAccess: true
parameter, I get an error response as follows:
GaxiosError: Invalid Value
at Gaxios._request (/Users/bteres/Projects/fw-docs/node_modules/gaxios/build/src/gaxios.js:85:23)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async JWT.requestAsync (/Users/bteres/Projects/fw-docs/node_modules/google-auth-library/build/src/auth/oauth2client.js:342:22) {
response: {
config: {
url: 'https://www.googleapis.com/drive/v3/drives?q=name%20%3D%20Clients&useDomainAdminAccess=true',
method: 'GET',
userAgentDirectives: [Array],
paramsSerializer: [Function],
headers: [Object],
params: [Object: null prototype],
validateStatus: [Function],
retry: true,
responseType: 'json',
retryConfig: [Object]
},
data: { error: [Object] },
headers: {
'alt-svc': 'h3-27=":443"; ma=2592000,h3-25=":443"; ma=2592000,h3-T050=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"',
'cache-control': 'private, max-age=0',
connection: 'close',
'content-encoding': 'gzip',
'content-security-policy': "frame-ancestors 'self'",
'content-type': 'application/json; charset=UTF-8',
date: 'Sun, 07 Jun 2020 10:43:54 GMT',
expires: 'Sun, 07 Jun 2020 10:43:54 GMT',
server: 'GSE',
'transfer-encoding': 'chunked',
vary: 'Origin, X-Origin',
'x-content-type-options': 'nosniff',
'x-frame-options': 'SAMEORIGIN',
'x-xss-protection': '1; mode=block'
},
status: 400,
statusText: 'Bad Request',
request: {
responseURL: 'https://www.googleapis.com/drive/v3/drives?q=name%20%3D%20Clients&useDomainAdminAccess=true'
}
},
config: {
url: 'https://www.googleapis.com/drive/v3/drives?q=name%20%3D%20Clients&useDomainAdminAccess=true',
method: 'GET',
userAgentDirectives: [ [Object] ],
paramsSerializer: [Function],
headers: {
'x-goog-api-client': 'gdcl/4.2.1 gl-node/12.17.0 auth/6.0.1',
'Accept-Encoding': 'gzip',
'User-Agent': 'google-api-nodejs-client/4.2.1 (gzip)',
Authorization: 'REDACTED',
Accept: 'application/json'
},
params: [Object: null prototype] {
q: 'name = Clients',
useDomainAdminAccess: true
},
validateStatus: [Function],
retry: true,
responseType: 'json',
retryConfig: {
currentRetryAttempt: 0,
retry: 3,
httpMethodsToRetry: [Array],
noResponseRetries: 2,
statusCodesToRetry: [Array]
}
},
code: 400,
errors: [
{
domain: 'global',
reason: 'invalid',
message: 'Invalid Value',
locationType: 'parameter',
location: 'q'
}
]
}
I tried leaving that out and only using the q
parameter and I get the same response. If I leave out the q
parameter, same problem.
I have been at this for a couple of days and any help would be greatly appreciated.
UPDATE:
I have already enabled domain-wide delegation for the service account as can be seen below.
I have also enabled this in my G-Suite admin API Management controls as seen below.
Did I possibly get some of the config incorrect here?
After hours of struggle and another nudge, I managed to figure this out. Google's API documentation is not so clear on this. Basically, you can use your service account but the service account need to impersonate a specific user, it is not by default an admin user in your domain even if you have all the configuration done.
So the easiest option I could figure out was to use google-auth's JWT credentials method as below:
const { google } = require("googleapis");
const credentials = require("./credentials.json");
const scopes = ["https://www.googleapis.com/auth/drive"];
const auth = new google.auth.JWT(
credentials.client_email,
null,
credentials.private_key,
scopes,
"admin@domain.com"
);
const drive = google.drive({ version: "v3", auth });
After changing the auth method to this, the query above works as expected.