使用Google Admin Directory API使用Google ServiceAccountCredential登录需要401

时间:2018-05-02 20:31:15

标签: google-api google-oauth google-admin-sdk google-api-dotnet-client gsuite

我试图按照此处列出的简单示例:https://developers.google.com/admin-sdk/directory/v1/quickstart/dotnet

不同之处在于我生成了一个服务帐户凭据,并将其指定为具有角色项目所有者的代理,因此它具有完全访问权限。我还为范围分配了适当的命名空间。

此处可以访问 orgunits ,这是我尝试在Directory API中列出的内容

scopes are assigned

这是我定义的服务帐户

enter image description here

以下是我的凭据

enter image description here

我下载了凭据的JSON并将其添加到我的项目中。我可以通过检查调试器来确认代码是否存在ServiceAccountCredential并成功进行身份验证并获取访问令牌。

然后我将凭证传递给服务初始化程序,当我创建并执行请求时,它失败并带有

{"Google.Apis.Requests.RequestError\r\nLogin Required [401]\r\nErrors [\r\n\tMessage[Login Required] Location[Authorization - header] Reason[required] Domain[global]\r\n]\r\n"}

以下是代码:

using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using System;
using System.Collections.Generic;
using System.IO;

namespace DirectoryQuickstart
{
    class Program
    {
        static string[] Scopes = { DirectoryService.Scope.AdminDirectoryUser, DirectoryService.Scope.AdminDirectoryOrgunit };
        static string ApplicationName = "slea-crm";
        static string Secret = "gsuite-secret.json";

        static void Main(string[] args)
        {

            ServiceAccountCredential sac = GoogleCredential.FromFile(Secret).CreateScoped(Scopes).UnderlyingCredential as ServiceAccountCredential;

            var token = sac.GetAccessTokenForRequestAsync().Result;

            // Create Directory API service.
            var service = new DirectoryService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = sac,

                ApplicationName = ApplicationName,
            });

            OrgunitsResource.ListRequest request = service.Orgunits.List(customerId: "REDACTED");
            IList<OrgUnit> orgUnits = request.Execute().OrganizationUnits;

            if (orgUnits != null && orgUnits.Count > 0)
            {
                foreach (var orgUnit in orgUnits)
                {
                    Console.WriteLine("{0} ({1})", orgUnit.Name, orgUnit.OrgUnitPath);
                }
            }
            else
            {
                Console.WriteLine("No orgunits found.");
            }
            Console.Read();

        }
    }
}

以下是我的JSON秘密内容(带有删节)

enter image description here

我在这里缺少什么?

编辑:好的,我在生成请求时断开了代码,我可以看到它没有在头部设置授权令牌承载的位置。为什么?我希望这个HttpClientInitializer类可以解决这个问题,因为API文档说它知道如何处理它,并且我发现的互联网上的每个例子都显示它只是将凭证传递给服务初始化器。但是当我走过它时,即使凭证已经被授予访问令牌并且其中存在一个访问令牌,请求也没有更新头部。

我唯一可以看到的是有一些方法可以添加一个HTTP请求拦截器,我自己也可以这样做,但哇,这看起来真的......奇怪 - 毕竟这些工作他们在dotnet客户端上做了SDK,老实说,我可以直接写入HTTP API,它可以更简单,更容易理解。

enter image description here

2 个答案:

答案 0 :(得分:2)

这个难题的缺失就是这一行:

ServiceAccountCredential sac = GoogleCredential.FromFile(Secret)
    .CreateScoped(Scopes)
    .UnderlyingCredential as ServiceAccountCredential;

需要修改为:

static string userName = "admin@yourdomain.com" // valid user in your org

ServiceAccountCredential sac = GoogleCredential.FromFile(Secret)
    .CreateScoped(Scopes)
    .CreateWithUser(userName)
    .UnderlyingCredential as ServiceAccountCredential;

Java / Python / Go做类似的示例在这里:https://developers.google.com/admin-sdk/directory/v1/guides/delegation#create_the_service_account_and_its_credentials

答案 1 :(得分:0)

已回答此问题,但在此处添加了更多详细信息。如果有人想冒充用户使用服务帐户将文件上传到Google驱动器上。请按照以下步骤

  • 创建服务帐户
  • 为服务帐户启用站点范围委派
  • 获取服务帐户客户ID
  • 通过Google管理控制台->管理API启用客户端ID以使用Google Drive API
  • 使用下面的C#代码上传文件

    public static DriveService GetService()
    {
        string[] scopes = new string[] { DriveService.Scope.Drive };
    
    
       //"SERVICE_ACCOUNT_EMAIL_HERE";
        String serviceAccountEmail = "test-417@elated-graph-261115.iam.gserviceaccount.com";
    
        // Scope and user email id which you want to impersonate
        var initializer = new ServiceAccountCredential.Initializer(serviceAccountEmail)
        {
            Scopes = scopes,
            User = "yourEmail@domain.com"
        };
    
        //get private key, from .JSON file
        var credential = new ServiceAccountCredential(initializer.FromPrivateKey("-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCkHeAicu6uFQn0\n7KUVTjgZ68nQui8+c8NmKW8aW8vhkBIKfdewXFECiUlTMPyI+HXbubsCK5Dl2xBS\nnphLq6YyE0xEQxNFLYHwfUKuzGQ2rV+qObcZ0mLZjCaf+pw3YiRVuU6OtslLJKJH\n-----END PRIVATE KEY-----\n"));
    
    
        // Create the service.
        var service = new DriveService(new BaseClientService.Initializer()
        {
            HttpClientInitializer = credential,
            ApplicationName = "DriveAPI",
        });
    
    
    
    
        service.HttpClient.Timeout = TimeSpan.FromMinutes(100);
        return service;
    }
    

就这样,我们已经完成了上面的代码,即使用模拟/委派通过服务帐户在Google云端硬盘上上传文件

参考:Upload file to Google Drive using Service Account in C# MVC (With Impersonation)