授权Google云端硬盘访问服务帐户失败

Mat*_*ach 14 c# google-drive-api

我参与构建了一个内部使用的应用程序,用户可以通过该应用程序上传文件,存储在Google云端硬盘中.由于建议不要将服务帐户用作文件所有者,因此我希望代表公司系统管理员可以访问的指定用户帐户上载应用程序.

我已经创建了应用程序以及服务帐户.为服务帐户创建了两个密钥,因为我尝试了尝试实现此目的的JSON和PKCS12格式:

API Manager凭据页面

我已经下载了OAuth 2.0客户端ID详细信息,并且还有服务帐户密钥的.json和.p12文件(按上面显示的顺序):

已下载客户端和服务帐户的文件

我让我的系统管理员完成了此处详细说明的步骤,将Drive API访问权限委托给服务帐户:https://developers.google.com/drive/v2/web/delegation#delegate_domain-wide_authority_to_your_service_account

我们发现在步骤4中唯一适用于"客户端名称"的是为Web应用程序列出的"客户端ID"(以.apps.googleusercontent.com结尾).为服务帐户密钥列出的长十六进制ID不是它所需的(见下文):

授权的API客户端访问权限

在上面的内容之前,我有一些代码可以创建一个可以直接上传到服务帐户的DriveService实例,引用服务帐户密钥的.json文件:

private DriveService GetServiceA()
{
    var settings = SettingsProvider.GetInstance();

    string keyFilePath = HostingEnvironment.MapPath("~/App_Data/keyfile.json");
    var scopes = new string[] { DriveService.Scope.Drive };

    var stream = new IO.FileStream(keyFilePath, IO.FileMode.Open, IO.FileAccess.Read);
    var credential = GoogleCredential.FromStream(stream);
    credential = credential.CreateScoped(scopes);

    var service = new DriveService(new BaseClientService.Initializer()
    {
        HttpClientInitializer = credential,
        ApplicationName = "MyAppName"
    });

    return service;
}
Run Code Online (Sandbox Code Playgroud)

这适用于列表和上传,但当然没有用于访问文件的Web UI,并且似乎它不处理诸如权限元数据或生成例如PDF的缩略图之类的内容.这就是我尝试使用标准帐户进行上传的原因.

一旦委托显然已经排序,我就尝试调整上面链接的委托引用中显示的代码,并结合来自其他地方的代码从.json密钥文件中提取必要的细节.使用此代码,只要我尝试执行任何API命令,即使如下这样简单:

FileList fileList = service.FileList().Execute();
Run Code Online (Sandbox Code Playgroud)

我收到一个错误:

异常详细信息:Google.Apis.Auth.OAuth2.Responses.TokenResponseException:错误:"unauthorized_client",描述:"请求中未经授权的客户端或范围.",Uri:""

该努力的代码是:

private DriveService GetServiceB()
{
  var settings = SettingsProvider.GetInstance();

  string keyFilePath = HostingEnvironment.MapPath("~/App_Data/keyfile.json");
  string serviceAccountEmail = "<account-email>@<project-id>.iam.gserviceaccount.com";
  var scopes = new string[] { DriveService.Scope.Drive };

  var stream = new IO.FileStream(keyFilePath, IO.FileMode.Open, IO.FileAccess.Read);
  var reader = new IO.StreamReader(stream);
  string jsonCreds = reader.ReadToEnd();
  var o = JObject.Parse(jsonCreds);
  string privateKey = o["private_key"].ToString();

  var credential = new ServiceAccountCredential(
    new ServiceAccountCredential.Initializer(serviceAccountEmail)
    {
      Scopes = scopes,
      User = "designated.user@sameappsdomain.com"
    }
    .FromPrivateKey(privateKey)
  );

  var service = new DriveService(new BaseClientService.Initializer()
  {
    HttpClientInitializer = credential,
    ApplicationName = "MyAppName"
  });

  return service;
}
Run Code Online (Sandbox Code Playgroud)

最后,我创建了第二个服务帐户密钥来保存.p12文件,以便更紧密地匹配权限委派文档中的代码,但这会导致相同的异常:

private DriveService GetServiceC()
{
  var settings = SettingsProvider.GetInstance();

  string p12KeyFilePath = HostingEnvironment.MapPath("~/App_Data/keyfile.p12");
  string serviceAccountEmail = "<account-email>@<project-id>.iam.gserviceaccount.com";
  var scopes = new string[] { DriveService.Scope.Drive }; // Full access

  X509Certificate2 certificate = new X509Certificate2(
    p12KeyFilePath,
    "notasecret",
    X509KeyStorageFlags.Exportable
  );

  var credential = new ServiceAccountCredential(
    new ServiceAccountCredential.Initializer(serviceAccountEmail)
    {
      Scopes = scopes,
      User = "designated.user@sameappsdomain.com"
    }
    .FromCertificate(certificate)
  );

  var service = new DriveService(new BaseClientService.Initializer()
  {
    HttpClientInitializer = credential,
    ApplicationName = "MyAppName"
  });

  return service;
}
Run Code Online (Sandbox Code Playgroud)

该方法所依赖的最小相关类是:

public class GoogleDrive
{
  public DriveService Service { get; private set; }

  public GoogleDrive()
  {
    this.Service = this.GetService();
  }

  private DriveService GetService()
  {
    // Code from either A, B or C
  }

  public FilesResource.ListRequest FileList()
  {
    return this.Service.Files.List();
  }
}
Run Code Online (Sandbox Code Playgroud)

这就是以这种方式使用的:

var service = new GoogleDrive();
FilesResource.ListRequest listRequest = service.FileList();
FileList fileList = listRequest.Execute();
Run Code Online (Sandbox Code Playgroud)

最后一行发生异常.

我不明白为什么我的服务帐户不能代表指定用户行事,指定用户是应用程序服务帐户应具有委派权限的域的一部分.我在这里误解了什么?

Mat*_*ach 7

我自己找到了答案,这配置,而不是代码.我与权限委派步骤共享的链接未提及创建服务帐户时可用的选项:一个复选框,表明该帐户将有资格进行域范围委派(DwD).

此链接更准确地描述了服务帐户的创建和委派:https://developers.google.com/identity/protocols/OAuth2ServiceAccount

我在创建服务帐户时不知道DwD,所以我没有选择该选项.可以返回并编辑服务帐户以进行选择.完成此操作后,我能够检索正确的客户端ID,以便在管理控制台的"管理API客户端访问"部分中使用.然后使用GetServiceC()方法按预期工作,我能够为同一Apps域中的用户检索文件.

这是一个复选框,需要勾选一个服务帐户才有资格进行域范围的授权:

创建新的服务帐户 -  DwD复选框

这是可用的额外信息,一旦你做到了这一点(有暴殄天物服务帐户旁边这并没有有框打勾,比较):

有和没有DwD的服务帐户的比较