使用 Google 管理目录 API 使用 Google ServiceAccountCredential 登录需要 401

zen*_*con 1 google-api google-oauth google-api-dotnet-client google-admin-sdk google-workspace

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

不同之处在于我生成了一个服务帐户凭据,并将其分配为具有角色项目所有者的委托,因此它具有完全访问权限。我还为它分配了适当的作用域名称空间。

在这里它可以访问Orgunits,这就是我试图在 Directory API 中列出的内容

范围已分配

这是我定义的服务帐户

在此输入图像描述

这是我的凭据

在此输入图像描述

我下载了凭证的 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"}
Run Code Online (Sandbox Code Playgroud)

这是代码:

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

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的 JSON 秘密的内容(经过编辑)

在此输入图像描述

我在这里缺少什么?

编辑:好的,我在生成请求时对代码进行断点,我可以看到它没有在标头中设置授权令牌承载者。为什么?我希望这个 HttpClientInitializer 类能够处理这个问题,因为 API 文档说它知道如何处理这个问题,并且我在互联网上找到的每个示例都表明它只是将凭据传递到服务初始值设定项中。但是,当我浏览它时,即使凭证已被授予访问令牌并且其中存在一个访问令牌,请求也没有更新标头。

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

在此输入图像描述

zen*_*con 7

拼图中缺少的一块是这一行:

ServiceAccountCredential sac = GoogleCredential.FromFile(Secret)
    .CreateScoped(Scopes)
    .UnderlyingCredential as ServiceAccountCredential;
Run Code Online (Sandbox Code Playgroud)

需要修改为这样:

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

ServiceAccountCredential sac = GoogleCredential.FromFile(Secret)
    .CreateScoped(Scopes)
    .CreateWithUser(userName)
    .UnderlyingCredential as ServiceAccountCredential;
Run Code Online (Sandbox Code Playgroud)

执行类似操作的 Java/Python/Go 示例如下:https ://developers.google.com/admin-sdk/directory/v1/guides/delegation#create_the_service_account_and_its_credentials