【问题标题】:Using a Service Account for Admin API with OAuth2 library in Google Apps Script to Add A User to a Google Group在 Google Apps 脚本中使用管理 API 的服务帐户和 OAuth2 库将用户添加到 Google 组
【发布时间】:2021-10-27 05:46:15
【问题描述】:

编辑:

URL 必须是https://admin.googleapis.com/admin/directory/v1/groups/{groupKey}/members 而不是https://admin.googleapis.com/admin/directory/v1/groups/{groupKey}/members/insert(没有insert,即使它是insert 方法)


我正在尝试使用 Google Apps 脚本构建一个脚本,该脚本能够将用户添加到具有服务帐户的 Google 群组。现在,使用AdminDirectory 服务很简单,所以这段代码可以工作:

const addUser = email => {
  const member = AdminDirectory.Members.insert(
    { email, role: 'MEMBER' },
    'test-group@domain.com'
  );
};

但是,此服务需要超级用户权限。因此我决定使用服务帐号。

我创建了服务帐号并通过the whole checklist

  1. 我生成了 JSON 密钥
  2. 启用域范围的委派
  3. 在管理控制台 > 安全 > API 控制 > 域范围委派中,我添加了以下范围:
  4. 然后我构建了这个使用OAuth2 library的解决方案:
const addUserWithServiceAccount = email => {
  const serviceAccount = {
    type: 'service_account',
    project_id: 'my-tools-324016',
    private_key_id: '021623b...',
    private_key: '-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----\n',
    client_email: 'SERVICE-ACCOUNT-EMAIL',
    client_id: '1059879019...',
    auth_uri: 'https://accounts.google.com/o/oauth2/auth',
    token_uri: 'https://oauth2.googleapis.com/token',
    auth_provider_x509_cert_url: 'https://www.googleapis.com/oauth2/v1/certs',
    client_x509_cert_url:
      'https://www.googleapis.com/robot/v1/metadata/x509/...',
  };

  const _getAdminService = serviceAccount => {
    return OAuth2.createService('admin-sdk-test')
      .setTokenUrl('https://accounts.google.com/o/oauth2/token')
      .setPrivateKey(serviceAccount.private_key)
      .setIssuer(serviceAccount.client_email)
      .setPropertyStore(PropertiesService.getScriptProperties())
      .setCache(CacheService.getUserCache())
      .setLock(LockService.getUserLock())
      .setScope([
        'https://www.googleapis.com/auth/admin.directory.group',
        'https://www.googleapis.com/auth/admin.directory.group.member',
        'https://www.googleapis.com/auth/admin.directory.user',
      ]);
  };

  const _executeWithAuth2 = (groupKey, email) => {
    console.log('_executeWithAuth2()');
    const adminService = _getAdminService(serviceAccount);
    if (!adminService.hasAccess()) {
      Logger.log('ERROR n' + adminService.getLastError());
      return;
    }

    const url = `https://admin.googleapis.com/admin/directory/v1/groups/${groupKey}/members/insert?memberKey=${email}`;

    const headers = {
      Authorization: `Bearer ${adminService.getAccessToken()}`,
      // 'Content-Type': 'application/json',
    };
    const options = {
      method: 'put',
      headers,
      contentType: 'application/json',
      payload: JSON.stringify({
        email,
        role: 'MEMBER',
        kind: 'admin#directory#member',
        type: 'USER',
      }),
      muteHttpExceptions: true,
    };

    console.log(`fetch(${url}, ${JSON.stringify(options, null, 2)})`);

    const result = UrlFetchApp.fetch(url, options).getContentText();
    console.log(result);
    return result;
  };

  return _executeWithAuth2('test-group@domain', email);
};

现在当我运行它时,我得到以下错误响应:

{
  "error": {
    "code": 403,
    "message": "Not Authorized to access this resource/api",
    "errors": [
      {
        "message": "Not Authorized to access this resource/api",
        "domain": "global",
        "reason": "forbidden"
      }
    ]
  }
}

当我将 PUT 切换到 POST 时,我得到:

在此服务器上找不到请求的 URL /admin/directory/v1/groups/test-group@domain.com/members/insert?memberKey=user@domain.com

但是,此方法使用服务帐户 works very well with BigQuery。尽管我无法找到特定于管理目录的解决方案,但我希望它能够以相同的方式工作。

我在这里错过了什么?

【问题讨论】:

    标签: google-apps-script google-cloud-platform oauth-2.0 service-accounts


    【解决方案1】:

    服务帐号也没有超级用户权限

    这就是您收到Not Authorized 错误的原因。

    • 服务帐户需要impersonate 具有相应权限的超级用户。
    • 为此,除了启用域范围的委派外,您还需要通过将模拟用户设置为subject.setSubject("EMAIL OF SUPER USER"),以编程方式指定模拟用户。

    示例:

    const _getAdminService = serviceAccount => {
        return OAuth2.createService('admin-sdk-test')
          .setSubject("EMAIL OF SUPER USER")
          .setTokenUrl('https://accounts.google.com/o/oauth2/token')
          .setPrivateKey(serviceAccount.private_key)
          .setIssuer(serviceAccount.client_email)
          .setPropertyStore(PropertiesService.getScriptProperties())
          .setCache(CacheService.getUserCache())
          .setLock(LockService.getUserLock())
          .setScope([
            'https://www.googleapis.com/auth/admin.directory.group',
            'https://www.googleapis.com/auth/admin.directory.group.member',
            'https://www.googleapis.com/auth/admin.directory.user',
          ]);
      };
    

    【讨论】:

    • 非常感谢您的贡献!这让我继续前进,但我现在遇到了另一个错误:Resource Not Found: memberKey。不知道我哪里出错了。
    • 这是一个不同的问题,但简而言之:memberKey 应该进入资源正文,而不是 URL。如果您展开 Try this API,您可以看到请求是如何针对 cURL、HTTP 和 Javascript 构建的。
    • 我可能有点慢,但我在您提供的链接中没有看到任何memberKey。他们拥有的唯一成员信息是他们的电子邮件,我已经知道了。
    • 有趣的是,我得到了 cUrl 脚本,即使它不会强制模仿
    • 我也不明白为什么POST 请求返回404 错误,而我认为我们可以同意cUrl 请求是POST 请求,但它没有问题。
    猜你喜欢
    • 2019-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-21
    • 1970-01-01
    • 1970-01-01
    • 2015-09-19
    • 2014-09-22
    相关资源
    最近更新 更多