Search code examples
google-apps-scriptoauth-2.0gmailgmail-apigoogle-workspace

Insufficient Permission error when setting Gmail delegate through service account


I have a service account with domain-wide delegation that I'm trying to use to setup a Gmail delegate via an App Script app. I'm using Google's OAuth2 library for App Script.

The source user (who will have their mail read and is being impersonated to perform the action) is a normal non-admin user. When running the code below, I get the following error about insufficient permissions:

{error={code=403.0, message=Insufficient Permission, errors=[{domain=global, reason=insufficientPermissions, message=Insufficient Permission}]}}

I can read the delegates of the same user just fine, I only get this error when trying to modify any property on their account via the API.

I've tried removing the app's permission in the My Account > Security section and re-accepting auth prompts (I do not get prompted to accept the Gmail prompt although I enabled it in the project to be safe - it's in the manifest).

I'm not sure what the problem is - there are countless StackOverflow questions with this same error code but none seem to have helped - most say to refresh the credentials.

  • The project with the service account has the Gmail API and Admin SDK enabled
  • The App Script app is set to Trusted in the Admin Console.
  • Domain-wide delegation is enabled on the service account
  • The client ID for the DWD service account is whitelisted in the admin console with all Gmail scopes.
  • I've also tried clearing out the authorizations that are stored in the script properties window.
  • All Google APIs for Gmail are whitelisted on the OAuth consent screen page

Here's my code:

function setGmailDelegateTest() {

  var boxEmail = '[email protected]';
  var userEmail = '[email protected]';
  var service = getGmailSharingService_(boxEmail);
  if (service.hasAccess()) {
    var url = 'https://www.googleapis.com/gmail/v1/users/' + boxEmail +'/settings/delegates';
    var response = UrlFetchApp.fetch(url, {
      method: 'post',
      headers: {
        Authorization: 'Bearer ' + service.getAccessToken()
      },
      body: {
        "delegateEmail": userEmail,
        "verificationStatus": "accepted"
      },
      muteHttpExceptions: true
    });
    var result = JSON.parse(response.getContentText());
    Logger.log(result);
  } else {
    Logger.log(service.getLastError());
  }
}
function getGmailSharingService_(boxEmail) { 
  return OAuth2.createService('gmail:' + boxEmail)
      .setTokenUrl('https://oauth2.googleapis.com/token')
      .setPrivateKey(PRIVATE_KEY)
      .setIssuer(CLIENT_EMAIL)
      .setSubject(boxEmail)
      .setPropertyStore(PropertiesService.getScriptProperties())
      .setScope('https://mail.google.com/ https://www.googleapis.com/auth/gmail.settings.sharing');
}

Solution

  • This is being encountered because the scope needs to be passed as an array as there are two scopes being passed. Please attempt with the below as your scope.

    .setScope(['https://mail.google.com/', 'https://www.googleapis.com/auth/gmail.settings.sharing']);
    

    Final Resolution

      var payload = {
          "delegateEmail": "[email protected]",
          "verificationStatus": "accepted"
      }
    
      var options = {
      "method" : "POST",
      "contentType": "application/json",
      "muteHttpExceptions": true,
      "headers" : {
        "Authorization" : 'Bearer ' + service.getAccessToken()
      },
      "payload" : JSON.stringify(payload)
      };
    
      var url = 'https://www.googleapis.com/gmail/v1/users/' + user + 
      '/settings/delegates';
    
      var response = UrlFetchApp.fetch(url, options);
      var json = response.getContentText();