如何使用服务账号访问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)是为终端用户身份凭证设计的接口,仅接受两种验证方式:
- API Key(你已经验证可用)
- 终端用户的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
相关产品推荐
相关产品推荐

