I am designing a Google Apps Script for domain-wide delegation, so that it can read details from Gmail in our Google Workspace domain's user accounts. The script fails even when it accesses my own Gmail. Thanks for looking at it!
"message": "Request had insufficient authentication scopes."
"errors ... ""message": "Insufficient Permission"
"reason": "ACCESS_TOKEN_SCOPE_INSUFFICIENT"
Being a super admin for our domain, I performed all the steps that follow.
My script is associated with a Google Cloud Platform (GCP) standard project.
I created a service account with access to the project. My roles: Project IAM Admin , Service account admin, Service account user, Service Account Token Creator.
The script includes the OAuth2 library.
OAuth 2.0 Scopes:
· "
https://www.googleapis.com/auth/script.external_request"
· "
https://www.googleapis.com/auth/gmail.labels" (Gmail API)
· "
https://www.googleapis.com/auth/admin.directory.user.readonly" (Admin SDK API)
Admin SDK API is for another function which I will test next; it is not used currently.
The above scopes are set as follows:
· Project manifest (appsscript.json)
· OAuth consent screen for the project
· Domain-wide delegation: Admin console > Security > API Controls > Domain-wide Delegation
· Enable APIs for the standard Google Cloud project: Gmail, Admin SDK
· The service account has "Trusted" access ( Admin console > Security > Access and data control > API controls > Manage Third-Party App Access > App Access Control ) to Gmail and external request, but not to Admin SDK (not currently used).
· Set scopes in Code.gs: setScope refers to the first two scopes (Admin SDK is not currently used).
Script:
// Email address of the user to impersonate.
var USER_EMAIL = 'i...@domain.org'; // use my address for testing the simplest case
function listLabels() {
/**
* References:
* https://developers.google.com/gmail/api/reference/rest/v1/users.labels/list
* https://gmail.googleapis.com/$discovery/rest?version=v1
* Similar script: https://developers.google.com/apps-script/advanced/gmail#list_label_information
*/
try {
const userID = '1x0gk371hhfknr'; // use my userID for now;
var service = getService_('listLabels');
if (service.hasAccess()) {
var url = 'https://gmail.googleapis.com/gmail/v1/users/' + userID + '/labels';
Logger.log(url);
// Script fails here:
var response = UrlFetchApp.fetch(
url, {
headers: { Authorization: 'Bearer ' + service.getAccessToken() },
muteHttpExceptions: true
}
);
Logger.log('response: '+ response);
var result = JSON.parse(response.getContentText());
Logger.log("result['labels']: " + JSON.stringify(result['labels'], null, 2));
for (let i = 0; i < result['labels'].length; i++) {
const label = result['labels'][i];
Logger.log(JSON.stringify(label));
}
} else {
Logger.log( 'Service lacks access' );
}