tholeb
tholeb

Reputation: 170

Access not granted or expired GMAIL API and Oauth2 / How to set email signatures using GMAIL API with a GSuite domain

i'm currently setting up an automatic signature system for every user of a google organisation but I haven't succeed to do so. I followed this guide but I have the following error : Service_.getAccessToken @ Service.gs:454. The problem is that: I don't have access to the file Service.gs and when I googled my error, no answers, either on google's github or stack overflow's website suits me well.

Here is the code (from the guide, I of course changed the auth values):

var accountsToIgnore = [
  '[email protected]',
  '[email protected]'
];

var auth = {
  "private_key": "-----BEGIN PRIVATE KEY-----\nABCDE\n-----END PRIVATE KEY-----\n",
  "client_email": "[email protected]",
  "client_id": "INSERT_CLIENT_ID_HERE"
};

function go() {  
  var pageToken;
  var page;

  do {
    page = AdminDirectory.Users.list({
      domain: 'example.com',
      orderBy: 'familyName',
      maxResults: 250,
      pageToken: pageToken,
      projection: 'full',
      // query: "[email protected]"
    });
    if (page.users) {
      page.users.forEach( function (user){
        if (accountsToIgnore.indexOf(user.primaryEmail) == -1) {

        var service = getOAuthService(user.primaryEmail);
        // Pull in the signatire template file contents into this variable 
        var signatureTemplate = HtmlService.createHtmlOutputFromFile("signature").getContent();

          // Set up a userData variable, with some blank defaults as backups  
          var userData = {
            email: user.primaryEmail,
            firstName: user.name.givenName,
            lastName: user.name.familyName,
            jobTitle: "",
            showJobTitle: true,
            workingHours: "",
            directPhone: ""
          };
          if (typeof user.customSchemas !== 'undefined') { // Email sig settings are set
            if (typeof user.customSchemas.Email_signature !== 'undefined') {

              if (typeof user.customSchemas.Email_signature.Show_job_title_in_signature !== 'undefined' && user.customSchemas.Email_signature.Show_job_title_in_signature == false) {
                userData.showJobTitle = false; 
              }

              if (typeof user.customSchemas.Email_signature.Working_Hours_Description !== 'undefined' && user.customSchemas.Email_signature.Working_Hours_Description != "") {
                userData.workingHours = "<br /><br /><i>"+user.customSchemas.Email_signature.Working_Hours_Description+"</i><br />";
              }

            }
          }

          if (user.hasOwnProperty('organizations') && user.organizations[0].hasOwnProperty('title') && typeof user.organizations[0].title !== "undefined" && userData.showJobTitle == true) {
            userData.jobTitle = user.organizations[0].title+"<br />";
          }

          if (user.hasOwnProperty('phones') && Array.isArray(user.phones) && user.phones.length >0) {
            for (var p = 0; p < user.phones.length; p++) {
              if (user.phones[p].customType == "Google Voice") {
              // Depending on where in the world you are, you may need to adjust this formatting for your own needs... This replaces the +44 UK country code with a local "0" and adds a space after the local area code for formatting.
               userData.directPhone = "<br />D: " + user.phones[p].value.replace('+44', '0').replace('1158', '1158 '); 
              }
            }
          }

          // Replace the placeholders as seen in the signature.html file with the actual data from the userData variable set up earlier. 
          var userSig = signatureTemplate
          .replace(/(\r\n|\n|\r)/gm, "")
          .replace(/{email}/g, userData.email)
          .replace(/{firstName}/g, userData.firstName)
          .replace(/{lastName}/g, userData.lastName)
          .replace(/{jobTitle}/g, userData.jobTitle)
          .replace(/{workingHours}/g, userData.workingHours)
          .replace(/{directNumber}/g, userData.directPhone); 

          var sigAPIUrl = Utilities.formatString('https://www.googleapis.com/gmail/v1/users/%s/settings/sendAs/%s',userData.email, userData.email);

          var response = UrlFetchApp.fetch(sigAPIUrl, {
            method: "PUT",
            muteHttpExceptions: true,
            contentType: "application/json",
            headers: {
              Authorization: 'Bearer ' + service.getAccessToken()
            },
            payload: JSON.stringify({
              'signature': userSig
            })
          });

          if (response.getResponseCode() !== 200) {
            Logger.log('There was an error: ' + response.getContentText());
          } else {
            Logger.log("Signature updated for "+user.primaryEmail);
          }
        }
      }); 

    } else {
      Logger.log('No users found.');
    }
    pageToken = page.nextPageToken;
  } while (pageToken);
}

function getOAuthService(userId) {
  return OAuth2.createService("Signature Setter "+userId)
  .setTokenUrl('https://accounts.google.com/o/oauth2/token')
  .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth') // Added thanks to research
  .setClientId(auth.client_id) // same
  .setCallbackFunction('authCallback') // same
  .setPrivateKey(auth.private_key)
  .setIssuer(auth.client_email)
  .setPropertyStore(PropertiesService.getScriptProperties())
  .setSubject(userId)
  .setParam('access_type', 'offline')
  .setScope('https://www.googleapis.com/auth/gmail.settings.basic https://www.googleapis.com/auth/gmail.settings.sharing');
}

The problem comes from the GMAIL API (100% errors on the google's API page) or from Google's OAuth2.

Here is some pages I found that are related to my problem:

Full error code from apps.google: Error: Access not granted or expired. Service_.getAccessToken @ Service.gs:454

The credentials API (the warning sign mean that it has been created automatically): https://prnt.sc/yy8kg5

The files and services I have on google's apps script editor: https://prnt.sc/yy9dyw

Thanks for the help.

Upvotes: 1

Views: 410

Answers (1)

tholeb
tholeb

Reputation: 170

Thanks to the answers above and some personal research, I managed to set up an automatic signature with Google Console and Gmail API.

This is a step by step guide on how to set up the system.

DISCLAIMER: I'm not a professionnal and I may do mistakes (so feel free to correct me if i'm wrong :) ), but I still want to make this "guide"... maybe it can help someone, who knows ?

1. GOOGLE APPS SCRIPTS

The steps are for the "New" Editor.

  1. Go to this link (google apps scripts) and create a new script.
  2. Go to Settings > Check "Show appsscript.json manifest file in editor"
  3. Editor > edit appsscript.json and add the oauthScopes:
{
...
"oauthScopes": [
      "https://www.googleapis.com/auth/gmail.settings.basic",
      "https://www.googleapis.com/auth/gmail.settings.sharing",
      "https://www.googleapis.com/auth/userinfo.email",
      "https://www.googleapis.com/auth/admin.directory.user",
      "https://www.googleapis.com/auth/script.external_request"
    ]
...
}
  1. In Editor tab: add the OAuth2 Library
  2. add the Gmail and AdminDirectory Services
  3. Copy this code (based on this page) and add your OAUTH2_SERVICE_ACCOUNT_PRIVATE_KEY and OAUTH2_SERVICE_ACCOUNT_CLIENT_EMAIL (you'll get it in the next part)
  4. Create an new file HTML file named signature.html
  5. In Settings tab under "GCP Projet": add your projet Number (you'll get it in the next part)

2. GCP Project

  1. Go to this link and create a project
  2. On the project Info card, you can see the "Project Number" copy it, and paste it in the Google script settings tab.
  3. Go to "APIs & Services" > Credentials > create a "Service Accounts" (you don't need roles)
  4. Edit that Service Account you just created and check "Enable G Suite domain-wide delegation" under "Service account status" and create a new json key (this will make you dowload a file, save it in a safe place, sharing this will result in a security leak in your G Suite domain (don't forget to add the OAUTH2_SERVICE_ACCOUNT_PRIVATE_KEY and OAUTH2_SERVICE_ACCOUNT_CLIENT_EMAIL in the script).

3. Google Admin (G Suite's Organization page)

  1. Go to Security > API Controls > "Manage Domain-wide delegation"
  2. Add new and add this scopes:
- https://www.googleapis.com/auth/gmail.settings.basic
- https://www.googleapis.com/auth/gmail.settings.sharing
- https://www.googleapis.com/auth/userinfo.email
- https://www.googleapis.com/auth/admin.directory.user
- https://www.googleapis.com/auth/script.external_request

4. Conclusion

It's now the part where you test the stuff, don't forget to create your signature via the signature.html file; look at the code.gs file to create something with user's info, it's pretty explicit.

If you have any problems, feel free to post a comment, i'll do my best to answer you.

Upvotes: 2

Related Questions