useDomainAdminAccess creates error in Google Drive API using service account authentication - google-drive-api

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.

Related

Why Does My Axios Error Message Not Include The Message I'd Receive From Postman?

Problem: Postman and Axios Error Messages are different, and I'd like to know how I can get the same error message in Axios as Postman gets.
Postman Error body:
{ "message": "Internal Error. Details are available in Magento log file. Report ID: webapi-xxxxxxxxxxxxx" }
Error status provided: 500 Internal Server Error
Axios Error Body:
{
message: 'Request failed with status code 500',
name: 'AxiosError',
stack: 'AxiosError: Request failed with status code 500\n' +
' at settle (/var/task/node_modules/axios/dist/node/axios.cjs:1261:12)\n' +
' at IncomingMessage.handleStreamEnd (/var/task/node_modules/axios/dist/node/axios.cjs:2444:11)\n' +
' at IncomingMessage.emit (node:events:539:35)\n' +
' at endReadableNT (node:internal/streams/readable:1345:12)\n' +
' at processTicksAndRejections (node:internal/process/task_queues:83:21)',
config: {
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
},
transformRequest: [ null ],
transformResponse: [ null ],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: { Blob: null },
headers: {
Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/json',
Authorization: 'OAuth oauth_consumer_key="XXXXXXXXXXXXXX",oauth_token="XXXXXXXXXXXXXX",oauth_signature_method="XXXXXXXXXXXXXX",oauth_timestamp="XXXXXXXXXXXXXX",oauth_nonce="XXXXXXXXXXXXXX",oauth_version="1.0",oauth_signature="XXXXXXXXXXXXXX="',
Cookie: 'XXXXXXXXXXXXXX=XXXXXXXXXXXXXX',
'User-Agent': 'axios/1.1.2',
'Content-Length': '1094',
'Accept-Encoding': 'gzip, deflate, br'
},
method: 'post',
url: 'XXXXXXXXXXXXXX',
params: {},
data: "XXXXXXXXXXXXXX",
'axios-retry': { retryCount: 0, lastRequestTime: 1676022167464 }
},
code: 'ERR_BAD_RESPONSE',
status: 500
}
It is evident the "Details are available in Magento" is not showing in the Axios Error. Why is that?

I am not able to give permission to created folder in google drive using google drive APIs

I have been trying to create a folder with a service account using google drive APIs in google drive. I can see that folder in the google folder list API but not in the google drive GUI.
Here is the code which I tried to create a google drive folder
const { google } = require('googleapis');
require('dotenv').config();
const CREDENTIALS_PATH = process.env.CREDENTIALS_PATH
const scopes = ['https://www.googleapis.com/auth/drive.appdata https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/drive']
module.exports = {
createFolder: async () => {
const auth = new google.auth.GoogleAuth({
scopes: scopes,
keyFilename: CREDENTIALS_PATH
})
const service = google.drive({ version: 'v3', auth: auth });
const fileMetadata = {
name: 'newFolder',
mimeType: 'application/vnd.google-apps.folder'
};
try {
const file = await service.files.create({
requestBody: fileMetadata,
fields: 'id',
});
if (!file.data.id) return;
const res2 = await service.permissions
.create({
resource: {
role: "owner",
type: "user",
emailAddress: email,
},
fileId: file.data.id,
transferOwnership: true,
supportsAllDrives: true
}).catch((err) => console.log(err));
console.log(res2.data);
return file.data.id;
} catch (err) {
throw err;
}
}
}
When I give permission to open a folder in drive then it throws an error.
Please find the below error:
GaxiosError: Bad Request. User message: "You cannot share this item because it has been flagged as
response: {
config: {
url: 'https://www.googleapis.com/drive/v3/files/15NJCr03yIXZ5Sv-xwK3ZiuW0UFTIk4IM/permissions?fields=%2A&transferOwnership=true&moveToNewOwnersRoot=true',
method: 'POST',
userAgentDirectives: [Array],
paramsSerializer: [Function (anonymous)],
data: [Object],
headers: [Object],
params: [Object],
validateStatus: [Function (anonymous)],
retry: true,
body: '{"type":"user","role":"owner","emailAddress":"########"}',
responseType: 'json',
retryConfig: [Object]
},
data: { error: [Object] },
headers: {
'alt-svc': 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":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-type': 'application/json; charset=UTF-8',
server: 'ESF',
'transfer-encoding': 'chunked',
vary: 'Origin, X-Origin',
'x-content-type-options': 'nosniff',
'x-frame-options': 'SAMEORIGIN',
'x-xss-protection': '0'
},
status: 400,
statusText: 'Bad Request',
request: {
responseURL: 'https://www.googleapis.com/drive/v3/files/15NJCr03yIXZ5Sv-xwK3ZiuW0UFTIk4IM/permissions?fields=%2A&transferOwnership=true&moveToNewOwnersRoot=true'
}
},
config: {
url: 'https://www.googleapis.com/drive/v3/files/15NJCr03yIXZ5Sv-xwK3ZiuW0UFTIk4IM/permissions?fields=%2A&transferOwnership=true&moveToNewOwnersRoot=true',
method: 'POST',
userAgentDirectives: [ [Object] ],
paramsSerializer: [Function (anonymous)],
data: {
type: 'user',
role: 'owner',
emailAddress: '#########'
},
headers: {
'x-goog-api-client': 'gdcl/6.0.3 gl-node/18.5.0 auth/8.7.0',
'Accept-Encoding': 'gzip',
'User-Agent': 'google-api-nodejs-client/6.0.3 (gzip)',
Authorization: 'Bearer #############################################',
'Content-Type': 'application/json',
Accept: 'application/json'
},
params: { fields: '*', transferOwnership: true, moveToNewOwnersRoot: true },
validateStatus: [Function (anonymous)],
retry: true,
body: '{"type":"user","role":"owner","emailAddress":"##############"}',
responseType: 'json',
retryConfig: {
currentRetryAttempt: 0,
retry: 3,
httpMethodsToRetry: [Array],
noResponseRetries: 2,
statusCodesToRetry: [Array]
}
},
code: 400,
errors: [
{
message: 'Bad Request. User message: "You cannot share this item because it has been flagged as inappropriate."',
domain: 'global',
reason: 'invalidSharingRequest'
}
]
}
Can you please help me to find that folder in google drive using google drive API?

NuxtJS - Google Auth: ExpiredAuthSessionError: Both token and refresh token have expired. Your request was aborted

Im using nuxtjs for a while. Before my last update all auth feature run smoothly.
But after I update the nuxt.config.js move axios plugin from plugins array to auth config object the error occurred. I need to put axios in auth config object because I need to access $auth from axios plugin.
Version
"#nuxtjs/auth-next": "5.0.0-1607693598.34d83ea"
"nuxt": "^2.15.2"
Nuxt configuration
Mode:
Universal
Nuxt configuration
auth: {
plugins: [{ src: "~/plugins/axios.js", ssr: true }], // <-- I put this, move from plugins array
strategies: {
local: {
token: {
property: "data.token",
required: true,
type: "",
maxAge: 18000,
},
user: {
property: "data",
autoFetch: true,
},
endpoints: {
login: { url: "/api/main", method: "post" },
logout: { url: "/api/profiles/logout", method: "get" },
user: { url: "/api/profile", method: "get" },
},
},
google: {
responseType: "code",
clientId:"<google client ID>",
codeChallengeMethod: "",
grantType: "authorization_code",
redirectUri: `${baseUrl}/verify-auth`,
},
},
redirect: {
login: "/login",
logout: "/login",
callback: "/login",
home: "/",
},
},
Reproduction
ExpiredAuthSessionError: Both token and refresh token have expired. Your request was aborted.
at a5d2e4e.js:1:4374
Steps to reproduce
After selecting a google account, then redirected to /verify-auth, then the error occurred. After /verify-auth it should go in.
Update google auth config like this:
google: {
responseType: "code",
clientId:"<google client ID>",
codeChallengeMethod: "",
grantType: "authorization_code",
redirectUri: `${baseUrl}/verify-auth`,
token: {
property: "data.token", // <-- based on your API response (endpints.login)
required: true,
type: "",
maxAge: 18000,
},
user: {
property: "data",
autoFetch: true,
},
endpoints: {
login: { url: "{your google auth API}", method: "post" }, // <-- should return response with token on it
logout: { url: "/api/profiles/logout", method: "get" },
user: { url: "/api/profile", method: "get" },
},
}

2 Legged Authorization Failure on Locked Down Network

I am very new to Forge and we are trying to run the tutorial on our work network.
Our network is very locked down so I have had to get the ‘developer.api.autodesk.com’ to the Cisco HTTPS Exceptions list.
Is there any other pages I need to add to get the 2 legged Authorization working.
When I click on 'Authorize Me' button on the local host I get 'Failed to Authorize' show up on the web page.
The report I get back on the node.js command prompt is as below (Client ID etc changed)
Any help appreciated.
> viewer-walkthrough-online.viewer#2.0.0 start C:\APA_Testing\Forge\walkthrough
> node start.js
Server listening on port 3000
{ Error: unable to get local issuer certificate
at TLSSocket.onConnectSecure (_tls_wrap.js:1058:34)
at TLSSocket.emit (events.js:198:13)
at TLSSocket._finishInit (_tls_wrap.js:636:8)
code: 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY',
config:
{ adapter: [Function: httpAdapter],
transformRequest: { '0': [Function: transformRequest] },
transformResponse: { '0': [Function: transformResponse] },
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
headers:
{ Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'axios/0.18.1',
'Content-Length': 186 },
method: 'post',
url:
'https://developer.api.autodesk.com/authentication/v1/authenticate',
data:
'client_id=XXXXXXXXXXXXXXXX&client_secret=XXXXXXXXXX&grant_type=client_credentials&scope=data%3Aread%20data%3Awrite%20data%3Acreate%20bucket%3Acreate%20bucket%3Aread' },
request:
Writable {
_writableState:
WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
bufferedRequest: null,
lastBufferedRequest: null,
pendingcb: 0,
prefinished: false,
errorEmitted: false,
emitClose: true,
autoDestroy: false,
bufferedRequestCount: 0,
corkedRequestsFree: [Object] },
writable: true,
_events:
[Object: null prototype] {
response: [Function: handleResponse],
error: [Function: handleRequestError] },
_eventsCount: 2,
_maxListeners: undefined,
_options:
{ protocol: 'https:',
maxRedirects: 21,
maxBodyLength: 10485760,
path: '/authentication/v1/authenticate',
method: 'post',
headers: [Object],
agent: undefined,
auth: undefined,
hostname: 'developer.api.autodesk.com',
port: null,
nativeProtocols: [Object],
pathname: '/authentication/v1/authenticate' },
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 186,
_requestBodyBuffers: [ [Object] ],
_onNativeResponse: [Function],
_currentRequest:
ClientRequest {
_events: [Object],
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: false,
_headerSent: true,
socket: [TLSSocket],
connection: [TLSSocket],
_header:
'POST /authentication/v1/authenticate HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nContent-Type: application/x-www-form-urlencoded\r\nUser-Agent: axios/0.18.1\r\nContent-Length: 186\r\nHost: developer.api.autodesk.com\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: [Agent],
socketPath: undefined,
timeout: undefined,
method: 'POST',
path: '/authentication/v1/authenticate',
_ended: false,
res: null,
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
_redirectable: [Circular],
[Symbol(isCorked)]: false,
[Symbol(outHeadersKey)]: [Object] },
_currentUrl:
'https://developer.api.autodesk.com/authentication/v1/authenticate' },
response: undefined }
The only host you need to whitelist is developer.api.autodesk.com - all our endpoints are on this domain.
Actually the error is complaining about not being able to locate the SSL certificates that should be present in your local vault to verify the connections - test if the error persists if you use tools like cURL/Postman to call our APIs and ask your network administrator for help on this.
As a temporary workaround absolutely not recommended due for security reasons you can disable check for SSL issues following here - simply add the code to the Axios config in start.js

React Native POST Method Issues

I have read many questions similar to mine but none solved my issue,
my fetch (Get Method) is working fine but the (POST METHOD) shows Unexpected end of JSON input
fetch('http://monasabat-app.com/Queries/sub_cat.php', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
id: 3
}),
}).then((response) => response.json())
.then((responseJson) => {
this.setState({
fsubCategory: responseJson.sub_cat
})
})
.catch((error) => {
console.error(error);
});
}
And this is the response I got by POSTMAN
{
"sub_cat": [
{
"id": "4",
"sub_cat": "sbu1",
"img_path": "url"
},
{
"id": "9",
"sub_cat": "sub2",
"img_path": "url"
}
]
}
Even if you try in postMan using Content-Type: application/json you will notice you don't get any response.
POST /Queries/sub_cat.php HTTP/1.1
Host: monasabat-app.com
Accept: application/json
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: c60774f8-1aaa-4cf8-8aa3-abcab34b05e3
{id:3}
The unexpected end of JSON input it's because it's trying to parse an empty string.
So it's not an issue related to fetch, just try the following to get the proper response:
fetch('http://monasabat-app.com/Queries/sub_cat.php', {
method: 'POST',
headers: {
'Content-Type':'application/x-www-form-urlencoded',
},
body: 'id=3',
})