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

调用GetAccessTokenForRequestAsync获取表格权限令牌失败,服务类调用正常

问题描述

使用Google.Apis.Auth.OAuth2的ServiceAccountCredential.GetAccessTokenForRequestAsync()方法时,出现以下异常场景:

  • 当作用域为https://www.googleapis.com/auth/spreadsheets或https://www.googleapis.com/auth/presentations时,调用($ServiceAccountCredential.GetAccessTokenForRequestAsync()).Result无返回结果,实际抛出unauthorized_client异常(该异常被PowerShell格式化工具屏蔽,需通过$Service.HttpClientInitializer.getAccessTokenForRequestAsync() | select *查看异步任务详情)
  • 切换为非文档类作用域(如https://www.googleapis.com/auth/drive)时,能正常获取访问令牌
  • 矛盾点:通过SheetsService内置资源类(如$Service.Spreadsheets.Get())可成功调用Sheets接口,说明服务账号的授权配置本身有效

示例代码:

[byte[]]$P12Key = $P12FromFile
$P12KeyPassword = 'notasecret'
$certificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new(
    [System.Byte[]]$P12Key,
    $P12KeyPassword,
    [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable
)

$ServiceAccountCredentialInitializer = [Google.Apis.Auth.OAuth2.ServiceAccountCredential+Initializer]::new('serviceaccount.iam.gserviceaccount.com')
$ServiceAccountCredentialInitializer.Scopes = [string[]]"https://www.googleapis.com/auth/spreadsheets"
$ServiceAccountCredentialInitializer.User = "impersonatedUser@example.com"
$ServiceAccountCredential = [Google.Apis.Auth.OAuth2.ServiceAccountCredential]::new($ServiceAccountCredentialInitializer.FromCertificate($certificate))


$ServiceInitializer = [Google.Apis.Services.BaseClientService+Initializer]::new()
$ServiceInitializer.HttpClientInitializer = $ServiceAccountCredential
$ServiceInitializer.ApplicationName = "Powershell Google Workspace"
$Service = [Google.Apis.Sheets.v4.SheetsService]::new($ServiceInitializer)

# 内置资源类调用成功
$Service.Spreadsheets.Get('167iXRp_a46lPHMndqUozuu0nQ') 

# 直接调用获取令牌无返回
($ServiceAccountCredential.GetAccessTokenForRequestAsync()).Result 

# 切换非文档类作用域可成功获取令牌
$ServiceAccountCredentialInitializer.Scopes = [string[]]"https://www.googleapis.com/auth/drive"
$ServiceAccountCredential = [Google.Apis.Auth.OAuth2.ServiceAccountCredential]::new($ServiceAccountCredentialInitializer.FromCertificate($certificate))
($ServiceAccountCredential.GetAccessTokenForRequestAsync()).Result 

抛出的异常信息:

Error:"unauthorized_client", 
Description:"Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.", 
Uri:""

原因分析

核心问题在于服务账号模拟用户(域范围委派)场景下,直接调用GetAccessTokenForRequestAsync()与内置服务类的令牌生成逻辑存在差异:

  • 内置服务类(如SheetsService)在调用API时,会自动处理令牌的受众(Audience)匹配逻辑,正确携带模拟用户上下文生成符合要求的令牌
  • 直接调用无参数的GetAccessTokenForRequestAsync()时,Auth库无法自动识别文档类API的特殊受众要求,导致生成的令牌不符合授权规则,触发unauthorized_client异常

解决方案

方案1:获取服务类缓存的有效令牌

既然内置服务类能正常调用API,可直接获取其已缓存的令牌,无需手动生成:

# 先调用一次服务接口确保令牌已缓存
$Service.Spreadsheets.Get('167iXRp_a46lPHMndqUozuu0nQ') | Out-Null

# 直接读取缓存的访问令牌
$cachedToken = $ServiceAccountCredential.Token.AccessToken
Write-Host "Cached Access Token: $cachedToken"

方案2:手动指定API受众调用

文档类API要求令牌的受众必须匹配API根地址,调用时传入目标API的根URL即可:

# 针对Sheets API生成令牌
$sheetsToken = ($ServiceAccountCredential.GetAccessTokenForRequestAsync("https://sheets.googleapis.com/")).Result
Write-Host "Sheets Access Token: $sheetsToken"

# 针对Slides API生成令牌
$slidesToken = ($ServiceAccountCredential.GetAccessTokenForRequestAsync("https://slides.googleapis.com/")).Result
Write-Host "Slides Access Token: $slidesToken"

方案3:确认域范围委派配置

虽然内置服务类能工作,仍需确保服务账号的域范围委派配置正确:

  • 在Google Workspace管理控制台中,已为服务账号启用域范围委派
  • 已将https://www.googleapis.com/auth/spreadsheets等文档类作用域添加到服务账号的授权列表
  • 模拟的用户账号拥有目标文档的访问权限

内容的提问来源于stack exchange,提问作者Phatmandrake

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 19:52:49