You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多账号场景下Apps Script从Rhino迁移至V8遇权限问题求助

解决Apps Script V8迁移中的多账号权限问题

作为Apps Script插件开发者,我们需在2026年1月31日前将基于Rhino引擎的脚本迁移至V8引擎。数年前尝试迁移时,因多账号相关的持续服务器错误(存储读取失败、权限代码错误)被迫退回Rhino,且无法要求客户使用无痕模式或登出所有Google账号。以下是可行的迁移解决方案:

可行解决方案

1. 错误处理中触发定向授权流程

在客户端失败处理器中检测到特定权限错误时,生成包含用户邮箱提示的授权链接,引导用户确认正确账号后重新授权:

修改index.html中的sayHello函数:

<script>
function sayHello() {
  google.script.run
    .withSuccessHandler(function(message) {
      document.getElementById('output').innerText = message;
    })
    .withFailureHandler(function(error) {
      if (error.message.includes("server error occurred while reading from storage") || error.message.includes("perm")) {
        google.script.run
          .withSuccessHandler(function(authUrl) {
            window.open(authUrl, '_blank');
            document.getElementById('output').innerText = '请在新窗口完成授权后,刷新当前页面';
          })
          .getAuthUrlWithHint();
      } else {
        document.getElementById('output').innerText = 'Error: ' + error.message;
        document.getElementById('output').style.color = 'red';
      }
    })
    .getHelloMessage();
}
</script>

在Code.gs中添加生成授权链接的函数:

function getAuthUrlWithHint() {
  var userEmail = Session.getActiveUser().getEmail();
  var authUrl = ScriptApp.getAuthorizationUrl() + '&login_hint=' + encodeURIComponent(userEmail);
  return authUrl;
}

2. 服务器端用户身份验证

在每个需要访问资源的服务器函数开头,验证当前活跃用户与预期用户(如文档所有者)是否一致,若不一致则抛出明确错误引导用户切换账号:

修改Code.gs中的getHelloMessage函数:

function getHelloMessage() {
  var activeUser = Session.getActiveUser().getEmail();
  var docOwner = SlidesApp.getActivePresentation().getOwner().getEmail();
  
  if (activeUser !== docOwner) {
    throw new Error('请使用文档所有者账号操作,或切换至正确账号后重试');
  }
  
  return "Hello, World from Apps Script!";
}

3. 升级为Web App模式(可选)

若侧边栏的多账号冲突无法解决,可将核心功能迁移为Web App,通过OAuth 2.0明确指定账号授权:

  • 将插件逻辑封装为Web App,设置合适的权限范围
  • 在侧边栏中通过iframe嵌入Web App,传递当前文档ID等参数
  • Web App使用明确的OAuth流程获取令牌,避免浏览器Cookie冲突

修改后的测试用例

appsscript.json

{
  "timeZone": "Europe/Prague",
  "dependencies": {},
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "oauthScopes": ["https://www.googleapis.com/auth/presentations", "https://www.googleapis.com/auth/userinfo.email"]
}

Code.gs

function onOpen() {
  var ui = SlidesApp.getUi();
  ui.createMenu('Custom Menu')
    .addItem('Open Sidebar', 'showSidebar')
    .addToUi();
}

function showSidebar() {
  var html = HtmlService.createHtmlOutputFromFile('index')
    .setTitle('My Sidebar')
    .setWidth(300);
  SlidesApp.getUi().showSidebar(html);
}

function getHelloMessage() {
  var activeUser = Session.getActiveUser().getEmail();
  var docOwner = SlidesApp.getActivePresentation().getOwner().getEmail();
  
  if (activeUser !== docOwner) {
    throw new Error('请使用文档所有者账号操作,或切换至正确账号后重试');
  }
  
  return "Hello, World from Apps Script!";
}

function getAuthUrlWithHint() {
  var userEmail = Session.getActiveUser().getEmail();
  var authUrl = ScriptApp.getAuthorizationUrl() + '&login_hint=' + encodeURIComponent(userEmail);
  return authUrl;
}

index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <h1>Hello Apps Script!</h1>
    <button onclick="sayHello()">Click Me</button>
    <p id="output"></p>

    <script>
      function sayHello() {
        document.getElementById('output').innerText = '加载中...';
        google.script.run
          .withSuccessHandler(function(message) {
            document.getElementById('output').innerText = message;
            document.getElementById('output').style.color = 'black';
          })
          .withFailureHandler(function(error) {
            if (error.message.includes("server error occurred while reading from storage") || error.message.includes("perm") || error.message.includes("请使用")) {
              google.script.run
                .withSuccessHandler(function(authUrl) {
                  window.open(authUrl, '_blank');
                  document.getElementById('output').innerText = '请在新窗口完成授权后,刷新当前页面';
                  document.getElementById('output').style.color = 'orange';
                })
                .getAuthUrlWithHint();
            } else {
              document.getElementById('output').innerText = 'Error: ' + error.message;
              document.getElementById('output').style.color = 'red';
            }
          })
          .getHelloMessage();
      }
    </script>
  </body>
</html>

内容的提问来源于stack exchange,提问作者tprieboj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 15:54:51