在 Google Apps 脚本中使用管理员服务帐户 API 和 OAuth2 库将用户添加到 Google 组

Using a Service Account for Admin API with OAuth2 library in Google Apps Script to Add A User to a Google Group


编辑:

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:
  5. 的解决方案
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 时,我得到:

The requested URL /admin/directory/v1/groups/test-group@domain.com/members/insert?memberKey=user@domain.com was not found on this server.

但是这种方法使用服务帐户 。尽管我找不到特定于管理目录的解决方案,但我希望它能以同样的方式工作。

我在这里错过了什么?

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

这就是您收到 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',
      ]);
  };