2

I have a Google Apps Script that gives me the error "Delegation denied for jasonjurotich@school.edu.mx", and is not working in order to add an alias (another email) to an account within a domain. It may be because of Token headers or some URL that is missing that authorizes something, but I cannot find enough documentation that clarifies how to add it.

This should not be confused with creating an alias in the Google Admin console for the same email. Rather, this is adding another separate account to the first account so as to send emails on behalf of the second account.

All the necessary permissions have been activated (OAuth2, a Google Service account with domain-wide delegation, necessary APIs are activated, etc.)

The script I have is the following:

var JSON = {
  "private_key": "key",
  "client_email": "email",
  "client_id": "ID",
  "user_email": "teststudent@school.edu.mx"
};

function getOAuthService(user) {
  return OAuth2.createService('Service Account')
    .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
    .setTokenUrl('https://accounts.google.com/o/oauth2/token')
    .setClientId('ID')
    .setPrivateKey(JSON.private_key)
    .setIssuer(JSON.client_email)
    .setSubject(JSON.user_email)
    .setPropertyStore(PropertiesService.getScriptProperties())
    .setParam('access_type', 'offline')
    .setParam('approval_prompt', 'force')
    .setScope('https://www.googleapis.com/auth/script.external_request https://www.googleapis.com/auth/gmail.settings.sharing');
}

function changeEmail() {
  var service = getOAuthService();
  service.reset();
  if (service.hasAccess()) {
    var userEmail = 'teststudent@school.edu.mx';   

    var alias = {
      sendAsEmail: 'aliastest1@school.edu.mx',
      displayName: 'TS',
      replyToAddress : 'aliastest1@school.edu.mx',
      treatAsAlias: true
    };

    Gmail.Users.Settings.SendAs.create(alias, userEmail);      
  }
}
Jason Jurotich
  • 441
  • 4
  • 24

2 Answers2

0

It seems that you are using the older version of OAuth2 library for Google Apps Script. Please check the source code and make sure it doesn't invoke ScriptApp.getProjectKey(). For example, the version below utilizes ScriptApp.getScriptId() instead of the deprecated method:

https://github.com/gsuitedevs/apps-script-oauth2/blob/master/dist/OAuth2.gs

Try connecting the new version to your GAS project as a library or manually add the files to your script and see if that fixes things.

UPDATE

I believe what happens is that you override a permissive authorization scope with a more restrictive one. Looking at the source code of the 'setScope()' method, it doesn't look like you can call it in succession.

Service_.prototype.setScope = function(scope, optSeparator) {
  var separator = optSeparator || ' ';
  this.params_.scope = Array.isArray(scope) ? scope.join(separator) : scope;
  return this;
};

Rather, you must provide the list of scopes and the optional separator (the default one is space). As a result, the only authorization scope your script ends up using is https://www.googleapis.com/auth/gmail.settings.basic. Bottom line: call 'setScope()' once, passing the space-separated list of required scopes. Instead of 2 separate calls, try this:

setScope('https://www.googleapis.com/auth/gmail.settings.basic https://www.googleapis.com/auth/gmail.settings.sharing');
Anton Dementiev
  • 5,451
  • 4
  • 20
  • 32
  • Updating the OAuth2 just got rid of the deprecation error, but the script still does not work. – Jason Jurotich Apr 09 '18 at 17:36
  • Anton Dementiev, sadly, I cannot find anything neither here in Stackoverflow nor on the internet that give concrete examples of how to use or apply Service_.prototypes. I do not know if I should open up another question about that, but I am ignorant of how to add that to the script. I believe that you are correct though. – Jason Jurotich Apr 09 '18 at 22:17
  • Could you please clarify the issue? You can simply open the source code of the OAuth2 library you are using and see the entire code in the 'dist' folder. It's written in Google Apps Script (JavaScript), so you can even rewrite the code as you see fit to make your own GAS library. Prototypes are a feature of JavaScript and are not unique to GAS https://developer.mozilla.org/en/docs/Web/JavaScript/Inheritance_and_the_prototype_chain – Anton Dementiev Apr 09 '18 at 22:23
  • Also, please read the documentation page for the OAuth2 library you are using. It basically says the same thing - namely, that you should call 'setScope()' only once https://github.com/gsuitedevs/apps-script-oauth2 – Anton Dementiev Apr 09 '18 at 22:27
  • Have you tried passing a space-separated list of oauth scopes, instead of calling the 'setScope()' method twice? See the example above – Anton Dementiev Apr 09 '18 at 22:33
  • I tried what you said, and in the debugging it says this: "({serviceName_:"Service Account", params_:{access_type:"offline", approval_prompt:"force", scope:"https://www.googleapis.com/auth/gmail.settings.basic"}, tokenFormat_:"application/json", tokenHeade..." and then says this "({error:"unauthorized_client", error_description:"Client is unauthorized to retrieve access tokens using this method.", granted_time:1523313340})" So I am presupposing that the only way is Service prototype, which I have no idea how to use. I am only using Google Apps Script to set this up. – Jason Jurotich Apr 09 '18 at 22:39
  • Did you put the 2nd scope (https://www.googleapis.com/auth/gmail.settings.sharing) after the space as shown in the example above? – Anton Dementiev Apr 09 '18 at 22:43
  • I just did that, and this came out: "({serviceName_:"Service Account", params_:{access_type:"offline", approval_prompt:"force", scope:"https://www.googleapis.com/auth/gmail.settings.basic https://www.googleapis.com/auth/gmail.settings...", with the same error as before. – Jason Jurotich Apr 09 '18 at 22:44
  • If you google this error, everything points to delegating domain-wide authority to the service account, so you may want to double check that. However, since you say you did this, I'm not sure if there's anything I can help you with https://stackoverflow.com/questions/42784640/client-is-unauthorized-to-retrieve-access-tokens-using-this-method-gmail-api-c-s – Anton Dementiev Apr 09 '18 at 22:51
  • Let me follow your indications and put the results here later on. – Jason Jurotich Apr 09 '18 at 22:52
  • Could you perhaps try adding the setClientId() call when creating the service object? Unfortunately, I can't test it, but the error description indicates this might work – Anton Dementiev Apr 10 '18 at 09:54
  • Ok, just went through everything from zero. Added the .setClientId(), and went over the permissions again, but I am definitely missing something. I do not think it is the scopes now, because the error for "Delegation denied for jasonjurotich@school.edu.mx" is now showing up, so something is wrong with the permissions, but it has to do with something in the script, because I know I have given it all necessary permissions. It might have to do with calling a URL, or some token headers, but I do not know. – Jason Jurotich Apr 10 '18 at 17:42
  • @JasonJurotich Of course, you are running the script under admin account and the account in question is within your domain? Ok, I'm officially out of ideas – Anton Dementiev Apr 10 '18 at 18:13
0

A colleague of mine posted something similar to this question here: Creates a custom "from" send-as alias with GAS and APIs. The following modified code is what finally worked taking into account what was put here and what they put there.

var service_account = {
"private_key": "-----BEGIN PRIVATE KEY-----",
"client_email": "changealiastest4@project-id-[].iam.gserviceaccount.com",
"client_id": "ID",
"user_email": "teststudent@school.edu.mx"
};

function getOAuthService(user) {
  return OAuth2.createService('Service Account')
    .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
    .setTokenUrl('https://accounts.google.com/o/oauth2/token')
    .setClientId('ID')
    .setPrivateKey(service_account.private_key)
    .setIssuer(service_account.client_email)
    .setSubject(service_account.user_email)
    .setPropertyStore(PropertiesService.getScriptProperties())
    .setParam('access_type', 'offline')
    .setParam('approval_prompt', 'force')
    .setScope('https://www.googleapis.com/auth/script.external_request https://www.googleapis.com/auth/gmail.settings.sharing https://www.googleapis.com/auth/gmail.settings.basic');
}

function changeEmail() {
  var userEmail = 'teststudent@school.edu.mx'; 
  var aliasEmail = 'aliastest1@school.edu.mx';
  var aliasName = 'TS';

  var service = getOAuthService();
    service.reset();
    if (service.hasAccess()) {

  var url = 'https://www.googleapis.com/gmail/v1/users/me/settings/sendAs'
    var headers ={
      "Authorization": 'Bearer ' + service.getAccessToken(),
      "Accept":"application/json", 
      "Content-Type":"application/json",
      };

  var resource ={
     sendAsEmail: aliasEmail,
     displayName: aliasName,
     replyToAddress : aliasEmail,
     treatAsAlias: true,
     verificationStatus: 'accepted'
   };  

  var options = {
  'headers': headers,
  'method': 'POST',
  'payload': JSON.stringify(resource),
  'muteHttpExceptions': true
  };

Logger.log(options);
var response = UrlFetchApp.fetch(url, options);
Logger.log(response.getContentText()); 

  }
}
Jason Jurotich
  • 441
  • 4
  • 24