访问未授予或过期 GMAIL API 和 Oauth2 / 如何使用 GMAIL API 和 GSuite 域设置电子邮件签名

Posted

技术标签:

【中文标题】访问未授予或过期 GMAIL API 和 Oauth2 / 如何使用 GMAIL API 和 GSuite 域设置电子邮件签名【英文标题】:Access not granted or expired GMAIL API and Oauth2 / How to set email signatures using GMAIL API with a GSuite domain 【发布时间】:2021-05-13 23:49:01 【问题描述】:

我目前正在为 google 组织的每个用户设置一个自动签名系统,但我没有成功。我遵循this 指南,但出现以下错误:Service_.getAccessToken @ Service.gs:454。问题是:我无权访问文件Service.gs,当我用谷歌搜索我的错误时,没有答案,无论是在谷歌的 github 还是堆栈溢出的网站上都非常适合我。

这是代码(来自指南,我当然更改了身份验证值):

var accountsToIgnore = [
  'ignore-me@example.com',
  'noreply@example.com'
];

var auth = 
  "private_key": "-----BEGIN PRIVATE KEY-----\nABCDE\n-----END PRIVATE KEY-----\n",
  "client_email": "name@project-id-XXX.iam.gserviceaccount.com",
  "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=your.email@example.com"
    );
    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');


问题来自 GMAIL API(谷歌的 API 页面上 100% 错误)或谷歌的 OAuth2。

这是我发现的一些与我的问题相关的页面:

github Issue Stack Overflow

来自 apps.google 的完整错误代码:Error: Access not granted or expired. Service_.getAccessToken @ Service.gs:454

凭证 API(警告标志表示它是自动创建的): https://prnt.sc/yy8kg5

我在谷歌应用脚​​本编辑器上的文件和服务:https://prnt.sc/yy9dyw

感谢您的帮助。

【问题讨论】:

1) 你从哪里得到这个代码? - 换句话说,将不同的授权方法混合在一起是一团糟 2) 您不能反对来自用户 ID (getOAuthService) 的凭据。 3)您需要实施域范围的授权并正确设置服务帐户以进行授权。然后服务帐户假定用户的身份。要非常小心地保护该服务帐户。 4) 你标记了gmail。您使用的是 Gmail 还是 Google Workspace?只有后者支持委托。 developers.google.com/admin-sdk/directory/v1/guides/delegation 访问未授予或过期 我的评论部分错误。您需要域范围委派,但您不需要假设每个用户的身份。服务帐户确实需要委派来管理 Workspace 域并将签名添加到每个帐户。 【参考方案1】:

感谢以上答案和一些个人研究,我设法使用 Google Console 和 Gmail API 设置了自动签名。

这是有关如何设置系统的分步指南。

免责声明:我不是专业人士,我可能会犯错误(如果我错了,请随时纠正我 :)),但我仍然想做这个“指南”。 ..也许它可以帮助某人,谁知道?

1.谷歌应用脚​​本

这些步骤适用于“新”编辑器。

    转到link (google apps scripts) 并创建一个新脚本。 进入设置>勾选“在编辑器中显示appsscript.json清单文件” Editor > 编辑 appsscript.json 并添加 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"
    ]
...

    在编辑器选项卡中:添加 OAuth2 库 添加GmailAdminDirectory 服务 复制此code(基于this 页面)并添加您的OAUTH2_SERVICE_ACCOUNT_PRIVATE_KEYOAUTH2_SERVICE_ACCOUNT_CLIENT_EMAIL(您将在下一部分中得到它) 创建一个名为signature.html的新文件HTML文件 在“GCP Projet”下的“设置”标签中:添加您的项目编号(您将在下一部分中获得)

2。 GCP 项目

    转到此link 并创建一个项目 在项目信息卡上,您可以看到“项目编号”将其复制,然后将其粘贴到 Google 脚本设置选项卡中。 转到“API 和服务”> 凭据 > 创建一个“服务帐户”(您不需要角色) 编辑您刚刚创建的服务帐户并选中“服务帐户状态”下的“启用 G Suite 域范围委派”并创建一个新的json 密钥(这将使您下载文件,将其保存在一个安全的地方,分享它会导致 G Suite 域中的安全漏洞(不要忘记在脚本中添加 OAUTH2_SERVICE_ACCOUNT_PRIVATE_KEYOAUTH2_SERVICE_ACCOUNT_CLIENT_EMAIL)。

3. Google 管理员(G Suite 的组织页面)

    转到安全 > API 控制 > “管理域范围的委派” 添加新的并添加此范围:
- 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.结论

现在是您测试内容的部分,别忘了通过signature.html 文件创建您的签名;查看code.gs 文件以创建包含用户信息的内容,它非常明确。

如果您有任何问题,请随时发表评论,我会尽力为您解答。

【讨论】:

以上是关于访问未授予或过期 GMAIL API 和 Oauth2 / 如何使用 GMAIL API 和 GSuite 域设置电子邮件签名的主要内容,如果未能解决你的问题,请参考以下文章

Instagram API“关系”权限未授予

refresh_token 是不是应该刷新未过期的访问令牌

Google API Oauth php 永久访问

Gmail API 发送电子邮件

Javascript;使用 Gmail API 无服务器检索未读电子邮件

如果未授予/拒绝访问,则 navigator.geolocation.getCurrentPosition 超时