4

This is a follow-up to a previous question/answer I posted (How to use the Google Email Settings API and the OAuth2 for Apps Script Library to set email signatures for users in a Google Apps domain), but I'm creating a new question since the Email Settings API has been deprecated and the process is significantly different now.

As the administrator of a G Suite domain, how do you use the Gmail API to programmatically set the email signatures of users in your domain through Google Apps Script?

TheMaster
  • 45,448
  • 6
  • 62
  • 85
Employee
  • 2,231
  • 3
  • 33
  • 60

1 Answers1

11

This method uses the Gmail API, the OAuth2 for Apps Script library, and "Domain-wide Delegation of Authority", which is a way for G Suite admins to make API calls on behalf of users within their domain.

Step 1: Make sure the OAuth2 For Apps Script library is added to your project.

Step 2: Set up "Domain-Wide Delegation of Authority." There's a page here explaining how to do it for the Drive API, but it's pretty much the same for any Google API, including the Gmail API. Follow the steps on that page up to, and including, the "Delegate domain-wide authority to your service account" step.

Step 3: The code below includes how to set the signature after the previous steps are complete:

function setSignatureTest() {

  var email = 'test@test.com';

  var signature = 'test signature';

  var test = setSignature(email, signature);

  Logger.log('test result: ' + test);

}


function setSignature(email, signature) {

  Logger.log('starting setSignature');

  var signatureSetSuccessfully = false;

  var service = getDomainWideDelegationService('Gmail: ', 'https://www.googleapis.com/auth/gmail.settings.basic', email);

  if (!service.hasAccess()) {

    Logger.log('failed to authenticate as user ' + email);

    Logger.log(service.getLastError());

    signatureSetSuccessfully = service.getLastError();

    return signatureSetSuccessfully;

  } else Logger.log('successfully authenticated as user ' + email);

  var username = email.split("@")[0];

  var resource = { signature: signature };

  var requestBody                = {};
  requestBody.headers            = {'Authorization': 'Bearer ' + service.getAccessToken()};
  requestBody.contentType        = "application/json";
  requestBody.method             = "PUT";
  requestBody.payload            = JSON.stringify(resource);
  requestBody.muteHttpExceptions = false;

  var emailForUrl = encodeURIComponent(email);

  var url = 'https://www.googleapis.com/gmail/v1/users/me/settings/sendAs/' + emailForUrl;

  var maxSetSignatureAttempts     = 20;
  var currentSetSignatureAttempts = 0;

  do {

    try {

      currentSetSignatureAttempts++;

      Logger.log('currentSetSignatureAttempts: ' + currentSetSignatureAttempts);

      var setSignatureResponse = UrlFetchApp.fetch(url, requestBody);

      Logger.log('setSignatureResponse on successful attempt:' + setSignatureResponse);

      signatureSetSuccessfully = true;

      break;

    } catch(e) {

      Logger.log('set signature failed attempt, waiting 3 seconds and re-trying');

      Utilities.sleep(3000);

    }

    if (currentSetSignatureAttempts >= maxSetSignatureAttempts) {

      Logger.log('exceeded ' + maxSetSignatureAttempts + ' set signature attempts, deleting user and ending script');

      throw new Error('Something went wrong when setting their email signature.');

    }

  } while (!signatureSetSuccessfully);

  return signatureSetSuccessfully;

}

// these two things are included in the .JSON file that you download when creating the service account and service account key
var OAUTH2_SERVICE_ACCOUNT_PRIVATE_KEY  = '-----BEGIN PRIVATE KEY-----\nxxxxxxxxxxxxxxxxxxxxx\n-----END PRIVATE KEY-----\n';
var OAUTH2_SERVICE_ACCOUNT_CLIENT_EMAIL = 'xxxxxxxxxxxxxxxxxxxxx.iam.gserviceaccount.com';


function getDomainWideDelegationService(serviceName, scope, email) {

  Logger.log('starting getDomainWideDelegationService for email: ' + email);

  return OAuth2.createService(serviceName + email)
      // Set the endpoint URL.
      .setTokenUrl('https://accounts.google.com/o/oauth2/token')

      // Set the private key and issuer.
      .setPrivateKey(OAUTH2_SERVICE_ACCOUNT_PRIVATE_KEY)
      .setIssuer(OAUTH2_SERVICE_ACCOUNT_CLIENT_EMAIL)

      // Set the name of the user to impersonate. This will only work for
      // Google Apps for Work/EDU accounts whose admin has setup domain-wide
      // delegation:
      // https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority
      .setSubject(email)

      // Set the property store where authorized tokens should be persisted.
      .setPropertyStore(PropertiesService.getScriptProperties())

      // Set the scope. This must match one of the scopes configured during the
      // setup of domain-wide delegation.
      .setScope(scope);

}

Please note: the do-while loop with the maxSetSignatureAttempts and currentSetSignatureAttempts variables is not necessary. I added it because if you're trying to set signatures immediately after creating the Google account and assigning a G Suite license, sometimes the Gmail API returns an error as if the user wasn't created yet. That do-while loop basically waits 3 seconds if it gets an error, then tries again, up to x number of times. You shouldn't have that issue if you're setting signatures for existing users. Also, originally I just had a fixed 10-second sleep, but most of the time it didn't need to take that long, but other times it would still fail. So this loop is better than a fixed sleep amount.

Employee
  • 2,231
  • 3
  • 33
  • 60
  • Hi, can this be done without been an administrator of a G Suite domain? I just want to log into my gmail account to make a http request ... – kurokirasama Mar 28 '17 at 16:19
  • No, you wouldn't be able to do this, as "Domain-wide delegation" is specifically for G Suite Administrators. However, if you're just trying to set your own email signature, you should be able to do it without DWD. Get rid of the call to `getDomainWideDelegationService` and the first `if` statement, and change `service.getAccessToken()` to `ScriptApp.getOAuthToken()`. That should get a valid token for your own account. Let me know if that works. – Employee Mar 28 '17 at 22:04
  • Hi Mike, I tried the code above and it works. However, I am confuse as to why I get "GoogleJsonResponseException: Not Found" error when I use Gmail.Users.Settings.SendAs.patch() method instead of HTTP Request with UrlFetchApp to update signature of other users in the domain. For domain-wide delegation, service account, and OAuth related task, am I only allow to use Gmail REST interface using UrlFetchApp service to make the calls instead of the Gmail API service methods? – s2000coder Apr 11 '18 at 20:22
  • Yes, when using the Gmail Service in the Apps Script Editor (`GmailApp`), you can only authenticate as the active/effective user (the user running the script, either from within the editor or from a timed trigger). The Service Account with Domain-Wide Delegation is what allows you as an administrator to make API calls on behalf of other accounts in the domain, but that requires using the Gmail REST API and it will not work with the built-in GmailApp service. – Employee Apr 12 '18 at 22:45
  • Mike, I am referring to the Gmail API (in Advance Google Services) and not the GmailApp (in G Suite Services). According to the Gmail API reference, you can use the Gmail.Users.Settings.SendAs.patch() method as documented here: https://developers.google.com/gmail/api/v1/reference/users/settings/sendAs/patch I've tried this method with account service but it does not work. please see my detail question here: https://stackoverflow.com/questions/49784910/googlejsonresponseexception-not-found-error-when-using-gmail-api-sendas-patch-m Thanks for the reply. – s2000coder Apr 17 '18 at 21:42
  • Ok, I posted an answer to your other question [here](https://stackoverflow.com/a/49906769/2344177). – Employee Apr 18 '18 at 18:37
  • I wonder if there is a way to do the same via server-side service account delegation? So far all my attempts to do so end up with "Delegation denied for admin@example.com" – ykhrustalev Oct 30 '18 at 20:30
  • @ykhrustalev: Can you post a new question separate from this one with the code that you're trying? – Employee Oct 31 '18 at 19:54
  • @mike I seems have found myself https://gist.github.com/ykhrustalev/dfedbeb28a7dba641ea2b7c9bfbaa56f – ykhrustalev Oct 31 '18 at 20:48