You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Google API服务账号调用users.get遇401 Unauthorized错误求助

问题:服务账号调用Google Admin Directory API返回401 Unauthorized

通过OAuth 2.0客户端应用直接调用users.get接口正常,但需要浏览器授权不符合需求。改用服务账号后,已完成域范围委派、创建JSON密钥,成功获取令牌但请求始终返回401错误。

错误信息

System.AggregateException
  HResult=0x80131500
  Message=Um ou mais erros.
  Source=mscorlib
  StackTrace:
   at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
   at googleteste.Program.Main(String[] args) in \source\repos\googleteste\googleteste\Program.cs:line 36

Inner Exception 1:
HttpRequestException: O código de status de resposta não indica êxito: 401 (Unauthorized).

相关代码

获取令牌的方法

public static async Task<string> GetAccessTokenFromJSONKeyAsync(string jsonKeyFilePath, params string[] scopes)
{
  using (var stream = new FileStream(jsonKeyFilePath, FileMode.Open, FileAccess.Read))
  {
    return await GoogleCredential
        .FromStream(stream) // Loads key file
        .CreateScoped(scopes) // Gathers scopes requested
        .UnderlyingCredential // Gets the credentials
        .GetAccessTokenForRequestAsync(); // Gets the Access Token
  }
}

public static string GetAccessTokenFromJSONKey(string jsonKeyFilePath, params string[] scopes)
{
  return GetAccessTokenFromJSONKeyAsync(jsonKeyFilePath, scopes).Result;
}

请求API的代码

var token = GoogleServiceAccount.GetAccessTokenFromJSONKeyAsync(
            "Keys/wneniac-2744e4a55337.json",
            new[] { "https://www.googleapis.com/auth/admin.directory.user", "https://www.googleapis.com/auth/admin.directory.user.security", "https://www.googleapis.com/auth/contacts", "https://www.googleapis.com/auth/gmail.modify" });

Console.WriteLine(new HttpClient().GetStringAsync($"https://admin-admin.googleapis.com/admin/directory/v1/users/usertosearch?access_token={token}&domain= ").Result);

解决方法

服务账号调用Admin Directory API时,必须模拟域内的管理员账号(Directory API需要管理员权限,服务账号本身无域内权限,需通过域范围委派模拟具体用户),这是导致401的核心原因。以下是具体修复步骤:

1. 修改令牌获取逻辑,添加用户模拟

在获取令牌时,通过CreateWithUser方法指定要模拟的域管理员邮箱(需为域内超级管理员账号)。修改后的方法:

public static async Task<string> GetAccessTokenFromJSONKeyAsync(string jsonKeyFilePath, string impersonateUserEmail, params string[] scopes)
{
  using (var stream = new FileStream(jsonKeyFilePath, FileMode.Open, FileAccess.Read))
  {
    return await GoogleCredential
        .FromStream(stream)
        .CreateScoped(scopes)
        .CreateWithUser(impersonateUserEmail) // 关键:添加用户模拟
        .UnderlyingCredential
        .GetAccessTokenForRequestAsync();
  }
}

2. 修正API请求地址与参数

  • 错误地址:https://admin-admin.googleapis.com,正确地址为https://admin.googleapis.com
  • domain参数不能留空,需填写实际域名
  • 避免用.Result阻塞异步方法,全程使用async/await

修改后的请求代码:

// 替换为你的域管理员邮箱和实际域名
var adminEmail = "admin@yourdomain.com";
var domain = "yourdomain.com";

var token = await GoogleServiceAccount.GetAccessTokenFromJSONKeyAsync(
    "Keys/wneniac-2744e4a55337.json",
    adminEmail,
    new[] { "https://www.googleapis.com/auth/admin.directory.user" }); // 仅保留必要权限

using (var httpClient = new HttpClient())
{
    var url = $"https://admin.googleapis.com/admin/directory/v1/users/usertosearch?domain={domain}";
    httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    var response = await httpClient.GetStringAsync(url);
    Console.WriteLine(response);
}

3. 验证域范围委派配置

确认以下配置无误:

  • 服务账号已在Google Cloud控制台开启域范围委派
  • 在Google Workspace管理控制台(admin.google.com)的「安全 -> API控制 -> 域范围委派」中,已添加服务账号的客户端ID,并授权了所需的API范围(如https://www.googleapis.com/auth/admin.directory.user)
  • 模拟的管理员账号为域内超级管理员,无API访问限制

内容的提问来源于stack exchange,提问作者Rodrigo da Silva Nascimento

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 13:05:38