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

如何使用服务账号访问Google Cloud Token Service API?遇403权限问题

问题:服务账号调用Google Cloud Token Service API出现403权限不足错误

我尝试通过服务账号调用Google Cloud Token Service API(接口地址:https://securetoken.googleapis.com/v1/token?key=[API_KEY]),参考身份平台的刷新令牌相关文档操作。但用服务账号生成的凭证调用时,持续返回403 FORBIDDEN错误,错误信息显示ACCESS_TOKEN_SCOPE_INSUFFICIENT(请求的身份验证范围不足)。

已确认以下事项:

  • GCP项目中Token Service API已启用
  • 服务账号拥有owner角色
  • 使用API Key调用该API完全正常

以下是我使用的Ruby代码,想请教问题原因,或者该API是否不支持服务账号调用?

SCOPE = %w[
  https://www.googleapis.com/auth/firebase
  https://www.googleapis.com/auth/identitytoolkit
]

def initialize
  decoded_service_account_json = Base64.decode64(ENV.fetch("GCP_JSON_KEYFILE_BASE64"))
  io = StringIO.new(decoded_service_account_json)
  @credentials = Google::Auth::DefaultCredentials.make_creds(scope: SCOPE, json_key_io: io)
end

# Exchange a refresh token for an ID token
def exchange_refresh_token(refresh_token)
  uri = URI.parse("https://securetoken.googleapis.com/v1/token")
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true

  request = Net::HTTP::Post.new(uri.path, { 'Content-Type' => 'application/x-www-form-urlencoded' })
  request.body = URI.encode_www_form(
    'grant_type' => 'refresh_token',
    'refresh_token' => refresh_token
  )

  request.add_field('Authorization', @credentials.apply({})[:authorization])

  response = http.request(request)
  response_data = JSON.parse(response.body)

  if response.code.to_i == 200
    {
      id_token: response_data['id_token'],
      refresh_token: response_data['refresh_token'],
      expires_at: Time.now + response_data['expires_in'].to_i
    }
  else
    raise TokenExchangeError.new("Token exchange failed: #{response.code} - #{response_data['error_description']}")
  end
end

解答

核心原因:该API不支持服务账号身份调用

Google Cloud Token Service API(securetoken.googleapis.com/v1/token)是为终端用户身份凭证设计的接口,仅接受两种验证方式:

  1. API Key(你已经验证可用)
  2. 终端用户的OAuth 2.0凭证(比如用户登录后获取的refresh_token,搭配对应的客户端ID/密钥)

服务账号属于服务身份,并非终端用户身份,即使配置了正确的scope或赋予高权限角色,也无法通过该接口完成令牌交换操作——这是API的设计限制,和权限配置无关。

代码调整建议

既然API Key调用正常,直接替换服务账号凭证的验证方式即可,修改后的代码示例:

def exchange_refresh_token(refresh_token)
  api_key = ENV.fetch("GCP_API_KEY") # 从环境变量获取API Key
  uri = URI.parse("https://securetoken.googleapis.com/v1/token?key=#{api_key}")
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true

  request = Net::HTTP::Post.new(uri.path, { 'Content-Type' => 'application/x-www-form-urlencoded' })
  request.body = URI.encode_www_form(
    'grant_type' => 'refresh_token',
    'refresh_token' => refresh_token
  )

  # 移除服务账号的Authorization头,改用URL中的API Key验证
  response = http.request(request)
  response_data = JSON.parse(response.body)

  if response.code.to_i == 200
    {
      id_token: response_data['id_token'],
      refresh_token: response_data['refresh_token'],
      expires_at: Time.now + response_data['expires_in'].to_i
    }
  else
    raise TokenExchangeError.new("Token exchange failed: #{response.code} - #{response_data['error_description']}")
  end
end

补充说明

如果你的业务场景必须使用服务账号完成身份相关操作,可以考虑使用Firebase Admin SDK或Identity Platform的服务端API(比如管理用户账号的接口),这些接口支持服务账号身份验证,但令牌交换这类面向终端用户的操作,仍需使用API Key或用户自身的凭证。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 02:20:27