无法授权Google云端硬盘访问服务帐户 [英] Failure of delegation of Google Drive access to a service account

查看:89
本文介绍了无法授权Google云端硬盘访问服务帐户的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

我让我的系统管理员执行了此处详细介绍的步骤,以委派Drive API访问服务帐户的权限:

之前,我有一段代码可以创建一个DriveService实例,该实例可以直接上传到服务帐户,并引用.json文件中的服务帐户密钥:

 私有DriveService GetServiceA(){var settings = SettingsProvider.GetInstance();字符串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);凭证=凭证.CreateScoped(范围);var service = new DriveService(new BaseClientService.Initializer(){HttpClientInitializer =凭据,ApplicationName ="MyAppName"});退货服务;} 

这适用于列出和上传,尽管当然没有用于访问文件的Web UI,而且似乎不处理诸如权限元数据或生成缩略图等问题.PDF文件.这就是为什么我要使用标准帐户进行上传的原因.

一旦对委派进行了明显排序,我便尝试修改上面链接的委派参考中显示的代码,并与其他地方的代码结合以从.json密钥文件中提取必要的细节.使用此代码,只要我尝试执行任何API命令,就很简单:

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

我收到一个错误:

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

该代码为:

 私有DriveService GetServiceB(){var settings = SettingsProvider.GetInstance();字符串keyFilePath = HostingEnvironment.MapPath(〜/App_Data/keyfile.json");字符串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 =新的IO.StreamReader(stream);字符串jsonCreds = reader.ReadToEnd();var o = JObject.Parse(jsonCreds);字符串privateKey = o ["private_key"].ToString();var credential = new ServiceAccountCredential(新的ServiceAccountCredential.Initializer(serviceAccountEmail){范围=范围,用户="designated.user@sameappsdomain.com"}.FromPrivateKey(privateKey));var service = new DriveService(new BaseClientService.Initializer(){HttpClientInitializer =凭据,ApplicationName ="MyAppName"});退货服务;} 

最后,我创建了第二个服务帐户密钥来保存.p12文件,以便与授权代理文档中的代码更加匹配,但是会导致相同的异常:

 私有DriveService GetServiceC(){var settings = SettingsProvider.GetInstance();字符串p12KeyFilePath = HostingEnvironment.MapPath(〜/App_Data/keyfile.p12");字符串serviceAccountEmail =< account-email> @< project-id> .iam.gserviceaccount.com";var scopes = new string [] {DriveService.Scope.Drive};//完全访问X509Certificate2证书=新的X509Certificate2(p12KeyFilePath,"notasecret",X509KeyStorageFlags.Exportable);var credential = new ServiceAccountCredential(新的ServiceAccountCredential.Initializer(serviceAccountEmail){范围=范围,用户="designated.user@sameappsdomain.com"}.FromCertificate(证书));var service = new DriveService(new BaseClientService.Initializer(){HttpClientInitializer =凭据,ApplicationName ="MyAppName"});退货服务;} 

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

 公共类GoogleDrive{公用DriveService服务{私人套装;}公开的GoogleDrive(){this.Service = this.GetService();}私有DriveService GetService(){//来自A,B或C的代码}公共FilesResource.ListRequest FileList(){返回this.Service.Files.List();}} 

并且以这种方式使用:

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

该异常发生在最后一行.

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

解决方案

我自己找到了答案,它是 配置,而不是代码.我与授权步骤"共享的链接没有提到创建服务帐户时可用的选项:一个复选框,表明该帐户可以进行域范围的授权(DwD).

此链接更准确地描述了服务帐户的创建和委派:

完成此操作后,这是可用的其他信息(旁边有一个一次性服务帐户,没有选中该复选框,以供比较):

的服务帐户的比较

I've been involved with building an internal-use application through which users may upload files, to be stored within Google Drive. As it is recommended not to use service accounts as file owners, I wanted to have the application upload on behalf of a designated user account, to which the company sysadmin has access.

I have created the application, along with a service account. There are two keys created for the service account, as I have tried both the JSON and PKCS12 formats trying to achieve this:

I have downloaded the OAuth 2.0 client ID details, and also have the .json and .p12 files for the service account keys (in that order as displayed above):

I had my sysadmin go through the steps detailed here to delegate authority for Drive API access to the service account: https://developers.google.com/drive/v2/web/delegation#delegate_domain-wide_authority_to_your_service_account

We found that the only thing that worked for the "Client name" in step 4 was the "Client ID" listed for the Web application (ending .apps.googleusercontent.com). The long hexadecimal IDs listed for the Service account keys were not what it required (see below):

Previously to the above, I had code which would create a DriveService instance that could upload directly to the service account, referencing the .json file for the service account keys:

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

That works for listing and uploading, though of course there's no web UI for access to the files, and it seems as though it doesn't handle things like permissions metadata or generation of thumbnails for e.g. PDFs. This is why I'm trying to use a standard account for the uploads.

Once the delegation was apparently sorted, I then attempted to adapt the code shown in the delegation reference linked above, combining with code from elsewhere for extracting the necessary details from the .json key file. With this code, as soon as I try to execute any API command, even as simple as:

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

I receive an error:

Exception Details: Google.Apis.Auth.OAuth2.Responses.TokenResponseException: Error:"unauthorized_client", Description:"Unauthorized client or scope in request.", Uri:""

The code for that effort is:

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

Finally, I created the second service account key to save a .p12 file in order to more closely match the code in the authority delegation documentation, but which results in the same exception:

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

The minimial relevant class where this method lives is:

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

And that's used in this fashion:

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

The exception occurs on that last line.

I do not understand why my service account cannot act on behalf of the designated user, which is part of the domain for which the application's service account should have delegated authority. What is it that I've misunderstood here?

解决方案

I have found the answer myself, and it was configuration, not code. The link I shared with the steps for delegation of authority does not mention an option available when creating the service account: a checkbox saying that the account will be eligible for domain-wide delegation (DwD).

This link describes the service account creation and delegation more accurately: https://developers.google.com/identity/protocols/OAuth2ServiceAccount

I did not know about DwD when I created the service account, and so I had not selected that option. It is possible to go back and edit a service account to select it. Once I did this, I was able to retrieve a correct client ID for use in the "Manage API Client Access" part of the admin console. Using the GetServiceC() method then works as intended, and I am able to retrieve files for users in the same Apps domain.

This is the checkbox that needs to be ticked for a service account to be eligible for domain-wide delegation of authority:

This is the extra information available once you've done that (with a throwaway service account alongside that did not have the box ticked, for comparison):

这篇关于无法授权Google云端硬盘访问服务帐户的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆