How to obtain authorization scopes for script run by a trigger?

1,328 views
Skip to first unread message

Martin Rysan

unread,
Jul 6, 2021, 2:05:41 PM7/6/21
to Google Apps Script Community
Hello everyone,

I am creating a script that creates a new user in my organization upon the submission of a Google Form with fields for the new user's info. Only certain accounts (non-admin) in the organization will have access to this form, and can thus create new users without being an admin.

The addUser function creates a new user as intended when I run it from the Google App Scripts Editor, since it's being run by my admin account. However, it won't run when I fill out the form and the script runs from the onFormSubmit() function.

The error I received is: GoogleJsonResponseException: API call to directory.users.insert failed with error: Request had insufficient authentication scopes.

As the error implies, it requires authentication scopes. I've read up on Oauth2 as much as I can, and have gone done a rabbit hole of various ways to grant authentication. The best way to obtain oath scopes for this scenario seems to be this: use App Scripts to obtain the OAuth2 token for you (which is good news). I've followed the example and used the ScriptApp.getOAuthToken() method, and then UrlFetchApp.fetch to try and get the required oath scope for the users.insert function.

The documentation for method users.insert says the required oath scopes are: https://www.googleapis.com/auth/admin.directory.user. First I tried just adding it to the manifest file (did not fix the error). Next, I tried making this the "url" for the UrlFetchApp.fetch method (see addUsers function below).

After this, the addUser() function still worked fine when running from Editor (after approving new scopes in the UI pop up window). But when I filled out the Google Form and it ran from onFormSubmit() I instead received this error: Exception: You do not have permission to call UrlFetchApp.fetch. Required permissions: https://www.googleapis.com/auth/script.external_request

At this point, I'm not sure what to do to obtain the scopes when the form runs from the onFormSubmit trigger. From my understanding, these authorization scopes either require admin level access, or an access token which is obtained by ScriptApp.getOAuthToken(). The https://www.googleapis.com/auth/script.external_request is in the manifest file as well, and I approved it. 

So then how could the script get the required authorization scopes when run by a trigger if it can't run the UrlFetchApp.fetch function (even though it's in the manifest) ?

Thank you!!
--------------------------

MANIFEST:

{
  "timeZone""America/New_York",
  "dependencies": {
    "enabledAdvancedServices": [
      {
        "userSymbol""AdminDirectory",
        "version""directory_v1",
        "serviceId""admin"
      }
    ]
  },
  "exceptionLogging""STACKDRIVER",
  "runtimeVersion""V8",
  "oauthScopes": [
  ]
}

ADD USERS FUNCTION:

function addUser(firstNamelastName) {

  var token = ScriptApp.getOAuthToken();

  var response = UrlFetchApp.fetch(url, {
    headers: {
      Authorization'Bearer ' + token
    }
  });
  

  var user = {
    primaryEmailfirstName + "." + lastName + "@" + domainURL,
    
    name: {
      givenNamefirstName,
      familyNamelastName
    },
    // Generate a random password string.
    passwordMath.random().toString(36)
  };
  user = AdminDirectory.Users.insert(user);
  Logger.log('User %s created with ID %s.'user.primaryEmailuser.id);
}

Alan Wells

unread,
Jul 6, 2021, 7:45:27 PM7/6/21
to Google Apps Script Community
Explicitly set a POST request.
Use a different url.
Use a payload.

I haven't tested the following code, but I made some changes that needed to be done.

function addUser(firstName, lastName) {
  var domainURL,options,url,user;
  
  
  user = {};
  user.primaryEmail = firstName + "." + lastName + "@" + domainURL;
  user.name = {
      givenName: firstName,
      familyName: lastName
    }
    
  user.password = Math.random().toString(36);

  options = {};
  options.method = "POST";
  options.muteHttpExceptions = true;//Make sure this is always set
  options.headers = {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
  options.payload = user;
  
  var response = UrlFetchApp.fetch(url, options);
  
  if (response && response.getResponseCode() !== 200) {//There should always be a response unless the
    //Fetch call failed and if that happens then the outer try - catch can handle it
    //Handle the error here
    
    return false;
  }

  response = response.getContentText();
  Logger.log(response)
  
  return true;
  
  /*
  user = AdminDirectory.Users.insert(user);
  Logger.log('User %s created with ID %s.', user.primaryEmail, user.id)
  */
}
Reply all
Reply to author
Forward
0 new messages