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

时间:2016-11-28 16:29:53

标签: c# google-drive-api

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

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

API Manager Credentials page

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

Downloaded files for client and service accounts

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

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

Authorized API client access

在上面的内容之前,我有一些代码可以创建一个可以直接上传到服务帐户的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;
}

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

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

FileList fileList = service.FileList().Execute();

我收到错误:

异常详细信息:Google.Apis.Auth.OAuth2.Responses.TokenResponseException:错误:" unauthorized_client",说明:"请求中未经授权的客户端或范围。",Uri:& #34;"

该努力的代码是:

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;
}

最后,我创建了第二个服务帐户密钥来保存.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;
}

此方法所在的最小相关类别是:

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();
  }
}

以这种方式使用:

var service = new GoogleDrive();
FilesResource.ListRequest listRequest = service.FileList();
FileList fileList = listRequest.Execute();

最后一行发生异常。

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

3 个答案:

答案 0 :(得分:7)

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

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

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

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

Creation of a new service account - DwD checkbox

这是您完成后可获得的额外信息(除了之前的一次性服务帐户,方框已打勾,以进行比较):

Comparison of service accounts with and without DwD

答案 1 :(得分:1)

在管理面板上创建服务帐户时,您可以勾选启用G Suite全域委派复选框。

此致

答案 2 :(得分:-2)

大多数一切看起来都不错,但是:

一个。使用ServiceC代码,不确定对象输入是否重要但是您的行:

var credential = new ServiceAccountCredential...

应该是

ServiceAccountCredential credential = new ServiceAccountCredential...

B中。检查ServiceC中的P12文件是否是您实际上传到运行此环境的环境中的真实P12文件。

℃。使用您用于创建和调用服务的确切可运行代码更新您的问题:filelist:execute code。通过这种方式,可以获得更清晰,更少的假设。