0

I'm sure there's something simple I'm missing here but I've been at this for over a week & can't get it figured out so I'm asking. I'll preface my question with I'm not a real programmer! I'm a network/systems administrator who needs to use Google Apps Script to configure signatures for all the users in our G Suite domain. I've had a fair bit of bash/command line/PowerShell experience but when it comes to "real" programming languages I know pretty much nothing.

That being said I was reading on another SO page here How to use the Gmail API, OAuth2 for Apps Script, and Domain-Wide Delegation to set email signatures for users in a G Suite domain about how to set email signatures. When I first tried the script I couldn't get it to work at all. I've modified it & managed to get it to authenticate now but nothing happens when it gets to the part where it should be setting the signatures, it just seems to quit & that's it! Here's my modified code minus any private bits:

// Adapted from script at https://stackoverflow.com/questions/40936257/how-to-use-the-gmail-api-oauth2-for-apps-script-and-domain-wide-delegation-to

// 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-----\n_MY_KEY_GOES_HERE_\n-----END PRIVATE KEY-----\n';
var OAUTH2_SERVICE_ACCOUNT_CLIENT_EMAIL = 'somethingsomethingsomething@project-id-xxxxxxxxxxxxxxxxxx.iam.gserviceaccount.com';

function setSignatureTest() {
  var email = 'user@domain.com';
  var signature = 'my 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', OAUTH2_SERVICE_ACCOUNT_CLIENT_EMAIL);
  if (!service.hasAccess()) {
    Logger.log('failed to authenticate as user ' + OAUTH2_SERVICE_ACCOUNT_CLIENT_EMAIL);
    Logger.log(service.getLastError());
    signatureSetSuccessfully = service.getLastError();
    return signatureSetSuccessfully;
  } else Logger.log('successfully authenticated as user ' + OAUTH2_SERVICE_ACCOUNT_CLIENT_EMAIL);
  var resource = { 'sendAsEmail' : email, 'userId' : OAUTH2_SERVICE_ACCOUNT_CLIENT_EMAIL, 'signature' : signature };
  var options = 
      {
        'method' : 'put',
        'contentType' : 'application/json',
        'Authorization' : 'Bearer ' + service.getAccessToken(),
        'payload' : resource
      };
  var emailForUrl = encodeURIComponent(email);
  var url = 'https://www.googleapis.com/gmail/v1/users/me/settings/sendAs/' + emailForUrl;
  var maxSetSignatureAttempts     = 1;
  var currentSetSignatureAttempts = 0;
  do {
    try {
      currentSetSignatureAttempts++;
      Logger.log('currentSetSignatureAttempts: ' + currentSetSignatureAttempts);
      var setSignatureResponse = UrlFetchApp.fetch(url, JSON.stringify(options));
      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');
      Logger.log('URL:  ' + url);
      Logger.log('Value of JSON.stringify(options):' + JSON.stringify(options));
      Logger.log('Value of setSignatureResponse:' + setSignatureResponse);
      throw new Error('Something went wrong when setting their email signature.');
    }
  } while (!signatureSetSuccessfully);
  return signatureSetSuccessfully;
}

function getDomainWideDelegationService(serviceName, scope, OAUTH2_SERVICE_ACCOUNT_CLIENT_EMAIL) {
  Logger.log('starting getDomainWideDelegationService for email: ' + OAUTH2_SERVICE_ACCOUNT_CLIENT_EMAIL);
  return OAuth2.createService(serviceName + OAUTH2_SERVICE_ACCOUNT_CLIENT_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(OAUTH2_SERVICE_ACCOUNT_CLIENT_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);
}

Anyone have any ideas on this? I'm sure someone else has done this before & I'm making a simple mistake somewhere. I feel like the problem is in my payload options but I'm really not sure how to troubleshoot this & everything I try does nothing.

Edit: Please see below for sanitized log output.

[17-04-24 18:24:27:087 PDT] starting setSignature
[17-04-24 18:24:27:088 PDT] starting getDomainWideDelegationService for email: somethingsomethingsomething@project-id-xxxxxxxxxxxxxxxxxxx.iam.gserviceaccount.com
[17-04-24 18:24:27:521 PDT] successfully authenticated as user somethingsomethingsomething@project-id-xxxxxxxxxxxxxxxxxxx.iam.gserviceaccount.com
[17-04-24 18:24:27:550 PDT] currentSetSignatureAttempts: 1
[17-04-24 18:24:27:552 PDT] set signature failed attempt, waiting 3 seconds and re-trying
[17-04-24 18:24:30:554 PDT] exceeded 1 set signature attempts, deleting user and ending script
[17-04-24 18:24:30:554 PDT] URL:  https://www.googleapis.com/gmail/v1/users/me/settings/sendAs/user%40domain.com
[17-04-24 18:24:30:555 PDT] Value of JSON.stringify(options):{"method":"put","contentType":"application/json","Authorization":"Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx”,”payload":{"sendAsEmail”:”user@domain.com”,”userId":"somethingsomethingsomething@project-id-xxxxxxxxxxxxxxxxxxx.iam.gserviceaccount.com","signature":"my test signature"}}
[17-04-24 18:24:30:556 PDT] Value of setSignatureResponse:undefined

Log output after making changes 2017.04.25.16:00:

[17-04-25 12:37:00:260 PDT] starting setSignature
[17-04-25 12:37:00:261 PDT] starting getDomainWideDelegationService for email: somethingsomethingsomething@project-id-xxxxxxxxxxxxxxxxxxx.iam.gserviceaccount.com
[17-04-25 12:37:00:278 PDT] successfully authenticated as user somethingsomethingsomething@project-id-xxxxxxxxxxxxxxxxxxx.iam.gserviceaccount.com
[17-04-25 12:37:00:289 PDT] currentSetSignatureAttempts: 1
[17-04-25 12:37:00:343 PDT] setSignatureResponse on successful attempt:{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "failedPrecondition",
    "message": "Bad Request"
   }
  ],
  "code": 400,
  "message": "Bad Request"
 }
}

[17-04-25 12:37:00:343 PDT] test result: true
Rubén
  • 34,714
  • 9
  • 70
  • 166
  • 1
    What does your log or execution transcript say? (View>Logs/Execution Transcript) – Jack Brown Apr 24 '17 at 22:22
  • Please see the edit I just made, thanks Jack! – user7916051 Apr 25 '17 at 01:29
  • You need to change this to a valid id `var email = 'user@domain.com';` perhaps your id to make sure it changes your signature – Jack Brown Apr 25 '17 at 05:25
  • I've replaced a real email address with "user@domain.com" here to hide the account. When I'm running the script I'm using my real email here. – user7916051 Apr 25 '17 at 15:05
  • Final suggestion i have is to add a the following `Logger.log(e.message)` in the 'catch(e) { }' block, to figure out what actually is our error. The code as is skips over the error and doesnt actually tell you what the error is. Equivalently you can remove try and catch(e) and let the function run and it will show you the error at runtime. – Jack Brown Apr 25 '17 at 16:10
  • I've added the suggested line `Logger.log(e.message);` just before `Utilities.sleep(3000);` and while I was hoping this was what I was looking for to get more information, all I'm seeing in the logs is "`[17-04-25 09:50:51:208 PDT] Bad value`". – user7916051 Apr 25 '17 at 17:03

2 Answers2

0

The authorization needs to go into the request headers like so

var options = 
      {
        'headers' : {'Authorization' : 'Bearer ' + service.getAccessToken()},
        'method' : 'put',
        'contentType' : 'application/json',
        'payload' : JSON.stringify(resource)
      };

Finally your call to the api will look like this:

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

Hope that resolves the problem

Jack Brown
  • 5,802
  • 2
  • 12
  • 27
  • That got me a lot closer! I'm now able to see some useful feedback in the logs after making your suggested changes & also adding `'muteHttpExceptions' : true;` to my options. I'm hoping I can figure out the rest after reviewing Google's API documentation. I'll append the current log output to my original question. – user7916051 Apr 25 '17 at 20:00
0

Try to change contenType to ctAPPLICATION_JSON:

"contentType": "ctAPPLICATION_JSON",
סטנלי גרונן
  • 2,917
  • 23
  • 46
  • 68